blob: 87597d5b1d8a715419db1e2e85bdc0f096c738ab [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* drivers/usb/gadget/f_diag.c
2 * Diag Function Device - Route ARM9 and ARM11 DIAG messages
3 * between HOST and DEVICE.
4 * Copyright (C) 2007 Google, Inc.
Anna Perel97b8c222012-01-18 10:08:14 +02005 * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07006 * Author: Brian Swetland <swetland@google.com>
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17#include <linux/init.h>
18#include <linux/module.h>
19#include <linux/kernel.h>
20#include <linux/platform_device.h>
21
22#include <mach/usbdiag.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070023
24#include <linux/usb/composite.h>
25#include <linux/usb/gadget.h>
26#include <linux/workqueue.h>
27#include <linux/debugfs.h>
28
29static DEFINE_SPINLOCK(ch_lock);
30static LIST_HEAD(usb_diag_ch_list);
31
32static struct usb_interface_descriptor intf_desc = {
33 .bLength = sizeof intf_desc,
34 .bDescriptorType = USB_DT_INTERFACE,
35 .bNumEndpoints = 2,
36 .bInterfaceClass = 0xFF,
37 .bInterfaceSubClass = 0xFF,
38 .bInterfaceProtocol = 0xFF,
39};
40
41static struct usb_endpoint_descriptor hs_bulk_in_desc = {
42 .bLength = USB_DT_ENDPOINT_SIZE,
43 .bDescriptorType = USB_DT_ENDPOINT,
44 .bEndpointAddress = USB_DIR_IN,
45 .bmAttributes = USB_ENDPOINT_XFER_BULK,
46 .wMaxPacketSize = __constant_cpu_to_le16(512),
47 .bInterval = 0,
48};
49static struct usb_endpoint_descriptor fs_bulk_in_desc = {
50 .bLength = USB_DT_ENDPOINT_SIZE,
51 .bDescriptorType = USB_DT_ENDPOINT,
52 .bEndpointAddress = USB_DIR_IN,
53 .bmAttributes = USB_ENDPOINT_XFER_BULK,
54 .wMaxPacketSize = __constant_cpu_to_le16(64),
55 .bInterval = 0,
56};
57
58static struct usb_endpoint_descriptor hs_bulk_out_desc = {
59 .bLength = USB_DT_ENDPOINT_SIZE,
60 .bDescriptorType = USB_DT_ENDPOINT,
61 .bEndpointAddress = USB_DIR_OUT,
62 .bmAttributes = USB_ENDPOINT_XFER_BULK,
63 .wMaxPacketSize = __constant_cpu_to_le16(512),
64 .bInterval = 0,
65};
66
67static struct usb_endpoint_descriptor fs_bulk_out_desc = {
68 .bLength = USB_DT_ENDPOINT_SIZE,
69 .bDescriptorType = USB_DT_ENDPOINT,
70 .bEndpointAddress = USB_DIR_OUT,
71 .bmAttributes = USB_ENDPOINT_XFER_BULK,
72 .wMaxPacketSize = __constant_cpu_to_le16(64),
73 .bInterval = 0,
74};
75
76static struct usb_descriptor_header *fs_diag_desc[] = {
77 (struct usb_descriptor_header *) &intf_desc,
78 (struct usb_descriptor_header *) &fs_bulk_in_desc,
79 (struct usb_descriptor_header *) &fs_bulk_out_desc,
80 NULL,
81 };
82static struct usb_descriptor_header *hs_diag_desc[] = {
83 (struct usb_descriptor_header *) &intf_desc,
84 (struct usb_descriptor_header *) &hs_bulk_in_desc,
85 (struct usb_descriptor_header *) &hs_bulk_out_desc,
86 NULL,
87};
88
89/**
90 * struct diag_context - USB diag function driver private structure
91 * @function: function structure for USB interface
92 * @out: USB OUT endpoint struct
93 * @in: USB IN endpoint struct
94 * @in_desc: USB IN endpoint descriptor struct
95 * @out_desc: USB OUT endpoint descriptor struct
96 * @read_pool: List of requests used for Rx (OUT ep)
97 * @write_pool: List of requests used for Tx (IN ep)
98 * @config_work: Work item schedule after interface is configured to notify
99 * CONNECT event to diag char driver and updating product id
100 * and serial number to MODEM/IMEM.
101 * @lock: Spinlock to proctect read_pool, write_pool lists
102 * @cdev: USB composite device struct
103 * @ch: USB diag channel
104 *
105 */
106struct diag_context {
107 struct usb_function function;
108 struct usb_ep *out;
109 struct usb_ep *in;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700110 struct list_head read_pool;
111 struct list_head write_pool;
112 struct work_struct config_work;
113 spinlock_t lock;
114 unsigned configured;
115 struct usb_composite_dev *cdev;
116 int (*update_pid_and_serial_num)(uint32_t, const char *);
117 struct usb_diag_ch ch;
118
119 /* pkt counters */
120 unsigned long dpkts_tolaptop;
121 unsigned long dpkts_tomodem;
122 unsigned dpkts_tolaptop_pending;
123};
124
125static inline struct diag_context *func_to_diag(struct usb_function *f)
126{
127 return container_of(f, struct diag_context, function);
128}
129
130static void usb_config_work_func(struct work_struct *work)
131{
132 struct diag_context *ctxt = container_of(work,
133 struct diag_context, config_work);
134 struct usb_composite_dev *cdev = ctxt->cdev;
135 struct usb_gadget_strings *table;
136 struct usb_string *s;
137
138 if (ctxt->ch.notify)
139 ctxt->ch.notify(ctxt->ch.priv, USB_DIAG_CONNECT, NULL);
140
141 if (!ctxt->update_pid_and_serial_num)
142 return;
143
144 /* pass on product id and serial number to dload */
145 if (!cdev->desc.iSerialNumber) {
146 ctxt->update_pid_and_serial_num(
147 cdev->desc.idProduct, 0);
148 return;
149 }
150
151 /*
152 * Serial number is filled by the composite driver. So
153 * it is fair enough to assume that it will always be
154 * found at first table of strings.
155 */
156 table = *(cdev->driver->strings);
157 for (s = table->strings; s && s->s; s++)
158 if (s->id == cdev->desc.iSerialNumber) {
159 ctxt->update_pid_and_serial_num(
160 cdev->desc.idProduct, s->s);
161 break;
162 }
163}
164
165static void diag_write_complete(struct usb_ep *ep,
166 struct usb_request *req)
167{
168 struct diag_context *ctxt = ep->driver_data;
169 struct diag_request *d_req = req->context;
170 unsigned long flags;
171
172 ctxt->dpkts_tolaptop_pending--;
173
174 if (!req->status) {
175 if ((req->length >= ep->maxpacket) &&
176 ((req->length % ep->maxpacket) == 0)) {
177 ctxt->dpkts_tolaptop_pending++;
178 req->length = 0;
179 d_req->actual = req->actual;
180 d_req->status = req->status;
181 /* Queue zero length packet */
182 usb_ep_queue(ctxt->in, req, GFP_ATOMIC);
183 return;
184 }
185 }
186
187 spin_lock_irqsave(&ctxt->lock, flags);
188 list_add_tail(&req->list, &ctxt->write_pool);
189 if (req->length != 0) {
190 d_req->actual = req->actual;
191 d_req->status = req->status;
192 }
193 spin_unlock_irqrestore(&ctxt->lock, flags);
194
195 if (ctxt->ch.notify)
196 ctxt->ch.notify(ctxt->ch.priv, USB_DIAG_WRITE_DONE, d_req);
197}
198
199static void diag_read_complete(struct usb_ep *ep,
200 struct usb_request *req)
201{
202 struct diag_context *ctxt = ep->driver_data;
203 struct diag_request *d_req = req->context;
204 unsigned long flags;
205
206 d_req->actual = req->actual;
207 d_req->status = req->status;
208
209 spin_lock_irqsave(&ctxt->lock, flags);
210 list_add_tail(&req->list, &ctxt->read_pool);
211 spin_unlock_irqrestore(&ctxt->lock, flags);
212
213 ctxt->dpkts_tomodem++;
214
215 if (ctxt->ch.notify)
216 ctxt->ch.notify(ctxt->ch.priv, USB_DIAG_READ_DONE, d_req);
217}
218
219/**
220 * usb_diag_open() - Open a diag channel over USB
221 * @name: Name of the channel
222 * @priv: Private structure pointer which will be passed in notify()
223 * @notify: Callback function to receive notifications
224 *
225 * This function iterates overs the available channels and returns
226 * the channel handler if the name matches. The notify callback is called
227 * for CONNECT, DISCONNECT, READ_DONE and WRITE_DONE events.
228 *
229 */
230struct usb_diag_ch *usb_diag_open(const char *name, void *priv,
231 void (*notify)(void *, unsigned, struct diag_request *))
232{
233 struct usb_diag_ch *ch;
234 struct diag_context *ctxt;
235 unsigned long flags;
236 int found = 0;
237
238 spin_lock_irqsave(&ch_lock, flags);
239 /* Check if we already have a channel with this name */
240 list_for_each_entry(ch, &usb_diag_ch_list, list) {
241 if (!strcmp(name, ch->name)) {
242 found = 1;
243 break;
244 }
245 }
246 spin_unlock_irqrestore(&ch_lock, flags);
247
248 if (!found) {
249 ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL);
250 if (!ctxt)
251 return ERR_PTR(-ENOMEM);
252
253 ch = &ctxt->ch;
254 }
255
256 ch->name = name;
257 ch->priv = priv;
258 ch->notify = notify;
259
260 spin_lock_irqsave(&ch_lock, flags);
261 list_add_tail(&ch->list, &usb_diag_ch_list);
262 spin_unlock_irqrestore(&ch_lock, flags);
263
264 return ch;
265}
266EXPORT_SYMBOL(usb_diag_open);
267
268/**
269 * usb_diag_close() - Close a diag channel over USB
270 * @ch: Channel handler
271 *
272 * This function closes the diag channel.
273 *
274 */
275void usb_diag_close(struct usb_diag_ch *ch)
276{
277 struct diag_context *dev = container_of(ch, struct diag_context, ch);
278 unsigned long flags;
279
280 spin_lock_irqsave(&ch_lock, flags);
281 ch->priv = NULL;
282 ch->notify = NULL;
283 /* Free-up the resources if channel is no more active */
284 if (!ch->priv_usb) {
285 list_del(&ch->list);
286 kfree(dev);
287 }
288
289 spin_unlock_irqrestore(&ch_lock, flags);
290}
291EXPORT_SYMBOL(usb_diag_close);
292
293/**
294 * usb_diag_free_req() - Free USB requests
295 * @ch: Channel handler
296 *
297 * This function free read and write USB requests for the interface
298 * associated with this channel.
299 *
300 */
301void usb_diag_free_req(struct usb_diag_ch *ch)
302{
303 struct diag_context *ctxt = ch->priv_usb;
304 struct usb_request *req;
305 struct list_head *act, *tmp;
306
307 if (!ctxt)
308 return;
309
310 list_for_each_safe(act, tmp, &ctxt->write_pool) {
311 req = list_entry(act, struct usb_request, list);
312 list_del(&req->list);
313 usb_ep_free_request(ctxt->in, req);
314 }
315
316 list_for_each_safe(act, tmp, &ctxt->read_pool) {
317 req = list_entry(act, struct usb_request, list);
318 list_del(&req->list);
319 usb_ep_free_request(ctxt->out, req);
320 }
321}
322EXPORT_SYMBOL(usb_diag_free_req);
323
324/**
325 * usb_diag_alloc_req() - Allocate USB requests
326 * @ch: Channel handler
327 * @n_write: Number of requests for Tx
328 * @n_read: Number of requests for Rx
329 *
330 * This function allocate read and write USB requests for the interface
331 * associated with this channel. The actual buffer is not allocated.
332 * The buffer is passed by diag char driver.
333 *
334 */
335int usb_diag_alloc_req(struct usb_diag_ch *ch, int n_write, int n_read)
336{
337 struct diag_context *ctxt = ch->priv_usb;
338 struct usb_request *req;
339 int i;
340
341 if (!ctxt)
342 return -ENODEV;
343
344 for (i = 0; i < n_write; i++) {
345 req = usb_ep_alloc_request(ctxt->in, GFP_ATOMIC);
346 if (!req)
347 goto fail;
348 req->complete = diag_write_complete;
349 list_add_tail(&req->list, &ctxt->write_pool);
350 }
351
352 for (i = 0; i < n_read; i++) {
353 req = usb_ep_alloc_request(ctxt->out, GFP_ATOMIC);
354 if (!req)
355 goto fail;
356 req->complete = diag_read_complete;
357 list_add_tail(&req->list, &ctxt->read_pool);
358 }
359
360 return 0;
361
362fail:
363 usb_diag_free_req(ch);
364 return -ENOMEM;
365
366}
367EXPORT_SYMBOL(usb_diag_alloc_req);
368
369/**
370 * usb_diag_read() - Read data from USB diag channel
371 * @ch: Channel handler
372 * @d_req: Diag request struct
373 *
374 * Enqueue a request on OUT endpoint of the interface corresponding to this
375 * channel. This function returns proper error code when interface is not
376 * in configured state, no Rx requests available and ep queue is failed.
377 *
378 * This function operates asynchronously. READ_DONE event is notified after
379 * completion of OUT request.
380 *
381 */
382int usb_diag_read(struct usb_diag_ch *ch, struct diag_request *d_req)
383{
384 struct diag_context *ctxt = ch->priv_usb;
385 unsigned long flags;
386 struct usb_request *req;
387
Manu Gautamb33f33a2011-09-09 14:35:57 +0530388 if (!ctxt)
389 return -ENODEV;
390
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700391 spin_lock_irqsave(&ctxt->lock, flags);
392
393 if (!ctxt->configured) {
394 spin_unlock_irqrestore(&ctxt->lock, flags);
395 return -EIO;
396 }
397
398 if (list_empty(&ctxt->read_pool)) {
399 spin_unlock_irqrestore(&ctxt->lock, flags);
400 ERROR(ctxt->cdev, "%s: no requests available\n", __func__);
401 return -EAGAIN;
402 }
403
404 req = list_first_entry(&ctxt->read_pool, struct usb_request, list);
405 list_del(&req->list);
406 spin_unlock_irqrestore(&ctxt->lock, flags);
407
408 req->buf = d_req->buf;
409 req->length = d_req->length;
410 req->context = d_req;
411 if (usb_ep_queue(ctxt->out, req, GFP_ATOMIC)) {
412 /* If error add the link to linked list again*/
413 spin_lock_irqsave(&ctxt->lock, flags);
414 list_add_tail(&req->list, &ctxt->read_pool);
415 spin_unlock_irqrestore(&ctxt->lock, flags);
416 ERROR(ctxt->cdev, "%s: cannot queue"
417 " read request\n", __func__);
418 return -EIO;
419 }
420
421 return 0;
422}
423EXPORT_SYMBOL(usb_diag_read);
424
425/**
426 * usb_diag_write() - Write data from USB diag channel
427 * @ch: Channel handler
428 * @d_req: Diag request struct
429 *
430 * Enqueue a request on IN endpoint of the interface corresponding to this
431 * channel. This function returns proper error code when interface is not
432 * in configured state, no Tx requests available and ep queue is failed.
433 *
434 * This function operates asynchronously. WRITE_DONE event is notified after
435 * completion of IN request.
436 *
437 */
438int usb_diag_write(struct usb_diag_ch *ch, struct diag_request *d_req)
439{
440 struct diag_context *ctxt = ch->priv_usb;
441 unsigned long flags;
442 struct usb_request *req = NULL;
443
Manu Gautamb33f33a2011-09-09 14:35:57 +0530444 if (!ctxt)
445 return -ENODEV;
446
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700447 spin_lock_irqsave(&ctxt->lock, flags);
448
449 if (!ctxt->configured) {
450 spin_unlock_irqrestore(&ctxt->lock, flags);
451 return -EIO;
452 }
453
454 if (list_empty(&ctxt->write_pool)) {
455 spin_unlock_irqrestore(&ctxt->lock, flags);
456 ERROR(ctxt->cdev, "%s: no requests available\n", __func__);
457 return -EAGAIN;
458 }
459
460 req = list_first_entry(&ctxt->write_pool, struct usb_request, list);
461 list_del(&req->list);
462 spin_unlock_irqrestore(&ctxt->lock, flags);
463
464 req->buf = d_req->buf;
465 req->length = d_req->length;
466 req->context = d_req;
467 if (usb_ep_queue(ctxt->in, req, GFP_ATOMIC)) {
468 /* If error add the link to linked list again*/
469 spin_lock_irqsave(&ctxt->lock, flags);
470 list_add_tail(&req->list, &ctxt->write_pool);
471 spin_unlock_irqrestore(&ctxt->lock, flags);
472 ERROR(ctxt->cdev, "%s: cannot queue"
473 " read request\n", __func__);
474 return -EIO;
475 }
476
477 ctxt->dpkts_tolaptop++;
478 ctxt->dpkts_tolaptop_pending++;
479
480 return 0;
481}
482EXPORT_SYMBOL(usb_diag_write);
483
484static void diag_function_disable(struct usb_function *f)
485{
486 struct diag_context *dev = func_to_diag(f);
487 unsigned long flags;
488
489 DBG(dev->cdev, "diag_function_disable\n");
490
491 spin_lock_irqsave(&dev->lock, flags);
492 dev->configured = 0;
493 spin_unlock_irqrestore(&dev->lock, flags);
494
495 if (dev->ch.notify)
496 dev->ch.notify(dev->ch.priv, USB_DIAG_DISCONNECT, NULL);
497
498 usb_ep_disable(dev->in);
499 dev->in->driver_data = NULL;
500
501 usb_ep_disable(dev->out);
502 dev->out->driver_data = NULL;
503
504}
505
506static int diag_function_set_alt(struct usb_function *f,
507 unsigned intf, unsigned alt)
508{
509 struct diag_context *dev = func_to_diag(f);
510 struct usb_composite_dev *cdev = f->config->cdev;
511 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700512 int rc = 0;
513
Tatyana Brokhman31ac3522011-06-28 15:33:50 +0200514 if (config_ep_by_speed(cdev->gadget, f, dev->in) ||
515 config_ep_by_speed(cdev->gadget, f, dev->out)) {
516 dev->in->desc = NULL;
517 dev->out->desc = NULL;
518 return -EINVAL;
519 }
520
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700521 dev->in->driver_data = dev;
Tatyana Brokhmancf709c12011-06-28 16:33:48 +0300522 rc = usb_ep_enable(dev->in);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700523 if (rc) {
524 ERROR(dev->cdev, "can't enable %s, result %d\n",
525 dev->in->name, rc);
526 return rc;
527 }
528 dev->out->driver_data = dev;
Tatyana Brokhmancf709c12011-06-28 16:33:48 +0300529 rc = usb_ep_enable(dev->out);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700530 if (rc) {
531 ERROR(dev->cdev, "can't enable %s, result %d\n",
532 dev->out->name, rc);
533 usb_ep_disable(dev->in);
534 return rc;
535 }
536 schedule_work(&dev->config_work);
537
Manu Gautam0c611072011-11-11 17:40:05 +0530538 dev->dpkts_tolaptop = 0;
539 dev->dpkts_tomodem = 0;
540 dev->dpkts_tolaptop_pending = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700541
542 spin_lock_irqsave(&dev->lock, flags);
543 dev->configured = 1;
544 spin_unlock_irqrestore(&dev->lock, flags);
545
546 return rc;
547}
548
549static void diag_function_unbind(struct usb_configuration *c,
550 struct usb_function *f)
551{
552 struct diag_context *ctxt = func_to_diag(f);
553
554 if (gadget_is_dualspeed(c->cdev->gadget))
555 usb_free_descriptors(f->hs_descriptors);
556
557 usb_free_descriptors(f->descriptors);
558 ctxt->ch.priv_usb = NULL;
559}
560
561static int diag_function_bind(struct usb_configuration *c,
562 struct usb_function *f)
563{
564 struct usb_composite_dev *cdev = c->cdev;
565 struct diag_context *ctxt = func_to_diag(f);
566 struct usb_ep *ep;
567 int status = -ENODEV;
568
569 intf_desc.bInterfaceNumber = usb_interface_id(c, f);
570
571 ep = usb_ep_autoconfig(cdev->gadget, &fs_bulk_in_desc);
Vamsi Krishnae606a7b2011-08-18 12:21:44 -0700572 if (!ep)
573 goto fail;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700574 ctxt->in = ep;
575 ep->driver_data = ctxt;
576
577 ep = usb_ep_autoconfig(cdev->gadget, &fs_bulk_out_desc);
Vamsi Krishnae606a7b2011-08-18 12:21:44 -0700578 if (!ep)
579 goto fail;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700580 ctxt->out = ep;
581 ep->driver_data = ctxt;
582
583 /* copy descriptors, and track endpoint copies */
584 f->descriptors = usb_copy_descriptors(fs_diag_desc);
585 if (!f->descriptors)
586 goto fail;
587
588 if (gadget_is_dualspeed(c->cdev->gadget)) {
589 hs_bulk_in_desc.bEndpointAddress =
590 fs_bulk_in_desc.bEndpointAddress;
591 hs_bulk_out_desc.bEndpointAddress =
592 fs_bulk_out_desc.bEndpointAddress;
593
594 /* copy descriptors, and track endpoint copies */
595 f->hs_descriptors = usb_copy_descriptors(hs_diag_desc);
596 }
597 return 0;
598fail:
599 if (ctxt->out)
600 ctxt->out->driver_data = NULL;
601 if (ctxt->in)
602 ctxt->in->driver_data = NULL;
603 return status;
604
605}
606
607int diag_function_add(struct usb_configuration *c, const char *name,
608 int (*update_pid)(uint32_t, const char *))
609{
610 struct diag_context *dev;
611 struct usb_diag_ch *_ch;
612 int found = 0, ret;
613
614 DBG(c->cdev, "diag_function_add\n");
615
616 list_for_each_entry(_ch, &usb_diag_ch_list, list) {
617 if (!strcmp(name, _ch->name)) {
618 found = 1;
619 break;
620 }
621 }
622 if (!found) {
623 ERROR(c->cdev, "unable to get diag usb channel\n");
624 return -ENODEV;
625 }
626
627 dev = container_of(_ch, struct diag_context, ch);
628 /* claim the channel for this USB interface */
629 _ch->priv_usb = dev;
630
Tatyana Brokhmancf709c12011-06-28 16:33:48 +0300631 dev->update_pid_and_serial_num = update_pid;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700632 dev->cdev = c->cdev;
633 dev->function.name = _ch->name;
634 dev->function.descriptors = fs_diag_desc;
635 dev->function.hs_descriptors = hs_diag_desc;
636 dev->function.bind = diag_function_bind;
637 dev->function.unbind = diag_function_unbind;
638 dev->function.set_alt = diag_function_set_alt;
639 dev->function.disable = diag_function_disable;
640 spin_lock_init(&dev->lock);
641 INIT_LIST_HEAD(&dev->read_pool);
642 INIT_LIST_HEAD(&dev->write_pool);
643 INIT_WORK(&dev->config_work, usb_config_work_func);
644
645 ret = usb_add_function(c, &dev->function);
646 if (ret) {
647 INFO(c->cdev, "usb_add_function failed\n");
648 _ch->priv_usb = NULL;
649 }
650
651 return ret;
652}
653
654
655#if defined(CONFIG_DEBUG_FS)
656static char debug_buffer[PAGE_SIZE];
657
658static ssize_t debug_read_stats(struct file *file, char __user *ubuf,
659 size_t count, loff_t *ppos)
660{
661 char *buf = debug_buffer;
662 int temp = 0;
663 struct usb_diag_ch *ch;
664
665 list_for_each_entry(ch, &usb_diag_ch_list, list) {
Jack Pham1c20e3f2012-04-04 19:29:14 -0700666 struct diag_context *ctxt = ch->priv_usb;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700667
Jack Pham1c20e3f2012-04-04 19:29:14 -0700668 if (ctxt)
669 temp += scnprintf(buf + temp, PAGE_SIZE - temp,
670 "---Name: %s---\n"
671 "endpoints: %s, %s\n"
672 "dpkts_tolaptop: %lu\n"
673 "dpkts_tomodem: %lu\n"
674 "pkts_tolaptop_pending: %u\n",
675 ch->name,
676 ctxt->in->name, ctxt->out->name,
677 ctxt->dpkts_tolaptop,
678 ctxt->dpkts_tomodem,
679 ctxt->dpkts_tolaptop_pending);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700680 }
681
682 return simple_read_from_buffer(ubuf, count, ppos, buf, temp);
683}
684
685static ssize_t debug_reset_stats(struct file *file, const char __user *buf,
686 size_t count, loff_t *ppos)
687{
688 struct usb_diag_ch *ch;
689
690 list_for_each_entry(ch, &usb_diag_ch_list, list) {
Jack Pham1c20e3f2012-04-04 19:29:14 -0700691 struct diag_context *ctxt = ch->priv_usb;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700692
Jack Pham1c20e3f2012-04-04 19:29:14 -0700693 if (ctxt) {
694 ctxt->dpkts_tolaptop = 0;
695 ctxt->dpkts_tomodem = 0;
696 ctxt->dpkts_tolaptop_pending = 0;
697 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700698 }
699
700 return count;
701}
702
703static int debug_open(struct inode *inode, struct file *file)
704{
705 return 0;
706}
707
708static const struct file_operations debug_fdiag_ops = {
709 .open = debug_open,
710 .read = debug_read_stats,
711 .write = debug_reset_stats,
712};
713
Manu Gautamd9f56a12011-09-26 14:04:49 +0530714struct dentry *dent_diag;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700715static void fdiag_debugfs_init(void)
716{
Manu Gautamd9f56a12011-09-26 14:04:49 +0530717 dent_diag = debugfs_create_dir("usb_diag", 0);
718 if (IS_ERR(dent_diag))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700719 return;
720
Manu Gautamd9f56a12011-09-26 14:04:49 +0530721 debugfs_create_file("status", 0444, dent_diag, 0, &debug_fdiag_ops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700722}
723#else
724static void fdiag_debugfs_init(void)
725{
726 return;
727}
728#endif
729
730static void diag_cleanup(void)
731{
732 struct diag_context *dev;
733 struct list_head *act, *tmp;
734 struct usb_diag_ch *_ch;
735 unsigned long flags;
736
Manu Gautamd9f56a12011-09-26 14:04:49 +0530737 debugfs_remove_recursive(dent_diag);
738
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700739 list_for_each_safe(act, tmp, &usb_diag_ch_list) {
740 _ch = list_entry(act, struct usb_diag_ch, list);
741 dev = container_of(_ch, struct diag_context, ch);
742
743 spin_lock_irqsave(&ch_lock, flags);
744 /* Free if diagchar is not using the channel anymore */
745 if (!_ch->priv) {
746 list_del(&_ch->list);
747 kfree(dev);
748 }
749 spin_unlock_irqrestore(&ch_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700750 }
751}
752
753static int diag_setup(void)
754{
755 fdiag_debugfs_init();
756
757 return 0;
758}