-/* Copyright (c) 2012-2014, 2016 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014,2016,2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
}
};
+static int hsic_buf_tbl_push(struct diag_hsic_info *ch, void *buf, int len)
+{
+ unsigned long flags;
+ struct diag_hsic_buf_tbl_t *item;
+
+ if (!ch || !buf || len < 0)
+ return -EINVAL;
+
+ item = kzalloc(sizeof(struct diag_hsic_buf_tbl_t), GFP_ATOMIC);
+ if (!item)
+ return -ENOMEM;
+ kmemleak_not_leak(item);
+
+ spin_lock_irqsave(&ch->lock, flags);
+ item->buf = buf;
+ item->len = len;
+ list_add_tail(&item->link, &ch->buf_tbl);
+ spin_unlock_irqrestore(&ch->lock, flags);
+
+ return 0;
+}
+
+static struct diag_hsic_buf_tbl_t *hsic_buf_tbl_pop(struct diag_hsic_info *ch)
+{
+ unsigned long flags;
+ struct diag_hsic_buf_tbl_t *item = NULL;
+
+ if (!ch || list_empty(&ch->buf_tbl))
+ return NULL;
+
+ spin_lock_irqsave(&ch->lock, flags);
+ item = list_first_entry(&ch->buf_tbl, struct diag_hsic_buf_tbl_t, link);
+ list_del(&item->link);
+ spin_unlock_irqrestore(&ch->lock, flags);
+
+ return item;
+}
+
+static void hsic_buf_tbl_clear(struct diag_hsic_info *ch)
+{
+ unsigned long flags;
+ struct list_head *start, *temp;
+ struct diag_hsic_buf_tbl_t *item = NULL;
+
+ if (!ch)
+ return;
+
+ /* At this point, the channel should already by closed */
+ spin_lock_irqsave(&ch->lock, flags);
+ list_for_each_safe(start, temp, &ch->buf_tbl) {
+ item = list_entry(start, struct diag_hsic_buf_tbl_t,
+ link);
+ list_del(&item->link);
+ kfree(item);
+
+ }
+ spin_unlock_irqrestore(&ch->lock, flags);
+}
+
static void diag_hsic_read_complete(void *ctxt, char *buf, int len,
int actual_size)
{
- int err = 0;
int index = (int)(uintptr_t)ctxt;
struct diag_hsic_info *ch = NULL;
*/
if (!ch->opened || actual_size <= 0)
goto fail;
- err = diag_remote_dev_read_done(ch->dev_id, buf, actual_size);
- if (err)
- goto fail;
+ hsic_buf_tbl_push(ch, buf, actual_size);
+ queue_work(ch->hsic_wq, &ch->read_complete_work);
return;
fail:
diagmem_init(driver, ch->mempool);
/* Notify the bridge that the channel is open */
diag_remote_dev_open(ch->dev_id);
+ INIT_LIST_HEAD(&ch->buf_tbl);
queue_work(ch->hsic_wq, &(ch->read_work));
return 0;
}
diag_bridge_close(ch->id);
diagmem_exit(driver, ch->mempool);
diag_remote_dev_close(ch->dev_id);
+ hsic_buf_tbl_clear(ch);
return 0;
}
queue_work(ch->hsic_wq, &ch->read_work);
}
+static void hsic_read_complete_work_fn(struct work_struct *work)
+{
+ struct diag_hsic_info *ch = container_of(work, struct diag_hsic_info,
+ read_complete_work);
+ struct diag_hsic_buf_tbl_t *item;
+
+ item = hsic_buf_tbl_pop(ch);
+ if (item) {
+ if (diag_remote_dev_read_done(ch->dev_id, item->buf, item->len))
+ goto fail;
+ }
+
+ kfree(item);
+ return;
+
+fail:
+ diagmem_free(driver, item->buf, ch->mempool);
+ queue_work(ch->hsic_wq, &ch->read_work);
+ kfree(item);
+}
+
static int diag_hsic_probe(struct platform_device *pdev)
{
unsigned long flags;
ch = &diag_hsic[i];
spin_lock_init(&ch->lock);
INIT_WORK(&(ch->read_work), hsic_read_work_fn);
+ INIT_WORK(&(ch->read_complete_work),
+ hsic_read_complete_work_fn);
INIT_WORK(&(ch->open_work), hsic_open_work_fn);
INIT_WORK(&(ch->close_work), hsic_close_work_fn);
strlcpy(wq_name, "DIAG_HSIC_", DIAG_HSIC_STRING_SZ);
-/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
#ifndef DIAGFWD_HSIC_H
#define DIAGFWD_HSIC_H
+
+#include <linux/list.h>
#ifdef CONFIG_DIAG_OVER_USB
#include <linux/usb/usbdiag.h>
#endif
#define DIAG_HSIC_NAME_SZ 24
+struct diag_hsic_buf_tbl_t {
+ struct list_head link;
+ unsigned char *buf;
+ int len;
+};
+
struct diag_hsic_info {
int id;
int dev_id;
uint8_t suspended;
char name[DIAG_HSIC_NAME_SZ];
struct work_struct read_work;
+ struct work_struct read_complete_work;
struct work_struct open_work;
struct work_struct close_work;
struct workqueue_struct *hsic_wq;
spinlock_t lock;
+ struct list_head buf_tbl;
};
extern struct diag_hsic_info diag_hsic[NUM_HSIC_DEV];