* [PATCH 2/2] drivers/net/usb/qcusbnet/qmidevice: Remove printhex, use print_hex_dump
From: Joe Perches @ 2010-09-29 2:39 UTC (permalink / raw)
To: Elly Jones; +Cc: netdev, dbrownell, mjg59, jglasgow, msb, olofj
In-Reply-To: <cover.1285727642.git.joe@perches.com>
Just use the normal kernel facility.
Signed-off-by: Joe Perches <joe@perches.com>
---
drivers/net/usb/qcusbnet/qmidevice.c | 34 ++++++++--------------------------
drivers/net/usb/qcusbnet/qmidevice.h | 1 -
2 files changed, 8 insertions(+), 27 deletions(-)
diff --git a/drivers/net/usb/qcusbnet/qmidevice.c b/drivers/net/usb/qcusbnet/qmidevice.c
index da7e42d..5150453 100755
--- a/drivers/net/usb/qcusbnet/qmidevice.c
+++ b/drivers/net/usb/qcusbnet/qmidevice.c
@@ -113,29 +113,6 @@ static bool device_valid(struct qcusbnet *dev)
return dev && dev->valid;
}
-void printhex(const void *data, size_t size)
-{
- const u8 *cdata = data;
- char *buf;
- size_t pos;
-
- buf = kmalloc(size * 3 + 1, GFP_ATOMIC);
- if (!buf) {
- DBG("Unable to allocate buffer\n");
- return;
- }
-
- memset(buf, 0 , size * 3 + 1);
-
- for (pos = 0; pos < size; pos++) {
- snprintf(buf + (pos * 3), 4, "%02X ", cdata[pos]);
- }
-
- DBG(" : %s\n", buf);
-
- kfree(buf);
-}
-
void qc_setdown(struct qcusbnet *dev, u8 reason)
{
set_bit(reason, &dev->down);
@@ -188,7 +165,8 @@ static void read_callback(struct urb *urb)
data = urb->transfer_buffer;
size = urb->actual_length;
- printhex(data, size);
+ print_hex_dump(KERN_INFO, "QCUSBNet2k: ", DUMP_PREFIX_OFFSET,
+ 16, 1, data, size, true);
result = qmux_parse(&cid, data, size);
if (result < 0) {
@@ -276,7 +254,10 @@ static void int_callback(struct urb *urb)
}
} else {
DBG("ignoring invalid interrupt in packet\n");
- printhex(urb->transfer_buffer, urb->actual_length);
+ print_hex_dump(KERN_INFO, "QCUSBNet2k: ",
+ DUMP_PREFIX_OFFSET, 16, 1,
+ urb->transfer_buffer,
+ urb->actual_length, true);
}
}
@@ -541,7 +522,8 @@ static int write_sync(struct qcusbnet *dev, char *buf, int size, u16 cid)
NULL, dev);
DBG("Actual Write:\n");
- printhex(buf, size);
+ print_hex_dump(KERN_INFO, "QCUSBNet2k: ", DUMP_PREFIX_OFFSET,
+ 16, 1, buf, size, true);
sema_init(&sem, 0);
diff --git a/drivers/net/usb/qcusbnet/qmidevice.h b/drivers/net/usb/qcusbnet/qmidevice.h
index 39a4b7f..5274a0d 100755
--- a/drivers/net/usb/qcusbnet/qmidevice.h
+++ b/drivers/net/usb/qcusbnet/qmidevice.h
@@ -22,7 +22,6 @@
#include "structs.h"
#include "qmi.h"
-void printhex(const void *data, size_t size);
void qc_setdown(struct qcusbnet *dev, u8 reason);
void qc_cleardown(struct qcusbnet *dev, u8 reason);
bool qc_isdown(struct qcusbnet *dev, u8 reason);
--
1.7.3.1.g432b3.dirty
^ permalink raw reply related
* [PATCH 1/2] drivers/net/usb/qcusbnet: Checkpatch cleanups
From: Joe Perches @ 2010-09-29 2:39 UTC (permalink / raw)
To: Elly Jones; +Cc: netdev, dbrownell, mjg59, jglasgow, msb, olofj
In-Reply-To: <cover.1285727642.git.joe@perches.com>
Whitespace and removal of KERNEL_VERSION tests
Neaten DBG macro
Signed-off-by: Joe Perches <joe@perches.com>
---
drivers/net/usb/qcusbnet/qcusbnet.c | 201 ++++++-----------
drivers/net/usb/qcusbnet/qmi.c | 32 ++--
drivers/net/usb/qcusbnet/qmidevice.c | 421 ++++++++++++++++-----------------
drivers/net/usb/qcusbnet/structs.h | 25 +--
4 files changed, 301 insertions(+), 378 deletions(-)
diff --git a/drivers/net/usb/qcusbnet/qcusbnet.c b/drivers/net/usb/qcusbnet/qcusbnet.c
index a075b55..54a354e 100644
--- a/drivers/net/usb/qcusbnet/qcusbnet.c
+++ b/drivers/net/usb/qcusbnet/qcusbnet.c
@@ -30,42 +30,33 @@ static struct class *devclass;
int qc_suspend(struct usb_interface *iface, pm_message_t event)
{
- struct usbnet * usbnet;
- struct qcusbnet * dev;
-
+ struct usbnet *usbnet;
+ struct qcusbnet *dev;
+
if (!iface)
return -ENOMEM;
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+
usbnet = usb_get_intfdata(iface);
-#else
- usbnet = iface->dev.platform_data;
-#endif
if (!usbnet || !usbnet->net) {
DBG("failed to get netdevice\n");
return -ENXIO;
}
-
+
dev = (struct qcusbnet *)usbnet->data[0];
if (!dev) {
DBG("failed to get QMIDevice\n");
return -ENXIO;
}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
- if (!usbnet->udev->auto_pm)
-#else
- if (!(event.event & PM_EVENT_AUTO))
-#endif
- {
- DBG("device suspended to power level %d\n",
- event.event);
+ if (!(event.event & PM_EVENT_AUTO)) {
+ DBG("device suspended to power level %d\n",
+ event.event);
qc_setdown(dev, DOWN_DRIVER_SUSPENDED);
} else {
DBG("device autosuspend\n");
}
-
+
if (event.event & PM_EVENT_SUSPEND) {
qc_stopread(dev);
usbnet->udev->reset_resume = 0;
@@ -73,31 +64,27 @@ int qc_suspend(struct usb_interface *iface, pm_message_t event)
} else {
usbnet->udev->reset_resume = 1;
}
-
+
return usbnet_suspend(iface, event);
}
-static int qc_resume(struct usb_interface * iface)
+static int qc_resume(struct usb_interface *iface)
{
struct usbnet *usbnet;
struct qcusbnet *dev;
int ret;
int oldstate;
-
+
if (iface == 0)
return -ENOMEM;
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+
usbnet = usb_get_intfdata(iface);
-#else
- usbnet = iface->dev.platform_data;
-#endif
if (!usbnet || !usbnet->net) {
DBG("failed to get netdevice\n");
return -ENXIO;
}
-
+
dev = (struct qcusbnet *)usbnet->data[0];
if (!dev) {
DBG("failed to get QMIDevice\n");
@@ -110,7 +97,7 @@ static int qc_resume(struct usb_interface * iface)
if (oldstate & PM_EVENT_SUSPEND) {
qc_cleardown(dev, DOWN_DRIVER_SUSPENDED);
-
+
ret = usbnet_resume(iface);
if (ret) {
DBG("usbnet_resume error %d\n", ret);
@@ -128,7 +115,7 @@ static int qc_resume(struct usb_interface * iface)
DBG("nothing to resume\n");
return 0;
}
-
+
return ret;
}
@@ -139,18 +126,18 @@ static int qcnet_bind(struct usbnet *usbnet, struct usb_interface *iface)
struct usb_host_endpoint *endpoint = NULL;
struct usb_host_endpoint *in = NULL;
struct usb_host_endpoint *out = NULL;
-
+
if (iface->num_altsetting != 1) {
DBG("invalid num_altsetting %u\n", iface->num_altsetting);
return -EINVAL;
}
if (iface->cur_altsetting->desc.bInterfaceNumber != 0) {
- DBG("invalid interface %d\n",
+ DBG("invalid interface %d\n",
iface->cur_altsetting->desc.bInterfaceNumber);
return -EINVAL;
}
-
+
numends = iface->cur_altsetting->desc.bNumEndpoints;
for (i = 0; i < numends; i++) {
endpoint = iface->cur_altsetting->endpoint + i;
@@ -158,7 +145,7 @@ static int qcnet_bind(struct usbnet *usbnet, struct usb_interface *iface)
DBG("invalid endpoint %u\n", i);
return -EINVAL;
}
-
+
if (usb_endpoint_dir_in(&endpoint->desc)
&& !usb_endpoint_xfer_int(&endpoint->desc)) {
in = endpoint;
@@ -166,14 +153,14 @@ static int qcnet_bind(struct usbnet *usbnet, struct usb_interface *iface)
out = endpoint;
}
}
-
+
if (!in || !out) {
DBG("invalid endpoints\n");
return -EINVAL;
}
if (usb_set_interface(usbnet->udev,
- iface->cur_altsetting->desc.bInterfaceNumber, 0)) {
+ iface->cur_altsetting->desc.bInterfaceNumber, 0)) {
DBG("unable to set interface\n");
return -EINVAL;
}
@@ -182,12 +169,9 @@ static int qcnet_bind(struct usbnet *usbnet, struct usb_interface *iface)
usbnet->out = usb_sndbulkpipe(usbnet->udev, out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
DBG("in %x, out %x\n",
- in->desc.bEndpointAddress,
+ in->desc.bEndpointAddress,
out->desc.bEndpointAddress);
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
- iface->dev.platform_data = usbnet;
-#endif
return 0;
}
@@ -197,23 +181,17 @@ static void qcnet_unbind(struct usbnet *usbnet, struct usb_interface *iface)
netif_carrier_off(usbnet->net);
qc_deregister(dev);
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
+
kfree(usbnet->net->netdev_ops);
usbnet->net->netdev_ops = NULL;
-#endif
-
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
- iface->dev.platform_data = NULL;
-#endif
kfree(dev);
}
-static void qcnet_urbhook(struct urb * urb)
+static void qcnet_urbhook(struct urb *urb)
{
unsigned long flags;
- struct worker * worker = urb->context;
+ struct worker *worker = urb->context;
if (!worker) {
DBG("bad context\n");
return;
@@ -244,7 +222,7 @@ static void qcnet_txtimeout(struct net_device *netdev)
DBG("failed to get usbnet device\n");
return;
}
-
+
dev = (struct qcusbnet *)usbnet->data[0];
if (!dev) {
DBG("failed to get QMIDevice\n");
@@ -283,7 +261,7 @@ static int qcnet_worker(void *arg)
DBG("passed null pointer\n");
return -EINVAL;
}
-
+
usbdev = interface_to_usbdev(worker->iface);
DBG("traffic thread started\n");
@@ -309,7 +287,7 @@ static int qcnet_worker(void *arg)
break;
}
-
+
spin_lock_irqsave(&worker->active_lock, activeflags);
if (IS_ERR(worker->active) && PTR_ERR(worker->active) == -EAGAIN) {
worker->active = NULL;
@@ -322,7 +300,7 @@ static int qcnet_worker(void *arg)
spin_unlock_irqrestore(&worker->active_lock, activeflags);
continue;
}
-
+
spin_lock_irqsave(&worker->urbs_lock, listflags);
if (list_empty(&worker->urbs)) {
spin_unlock_irqrestore(&worker->urbs_lock, listflags);
@@ -340,22 +318,18 @@ static int qcnet_worker(void *arg)
status = usb_autopm_get_interface(worker->iface);
if (status < 0) {
DBG("unable to autoresume interface: %d\n", status);
- if (status == -EPERM)
- {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
- usbdev->auto_pm = 0;
-#endif
+ if (status == -EPERM) {
qc_suspend(worker->iface, PMSG_SUSPEND);
}
spin_lock_irqsave(&worker->urbs_lock, listflags);
list_add(&req->node, &worker->urbs);
spin_unlock_irqrestore(&worker->urbs_lock, listflags);
-
+
spin_lock_irqsave(&worker->active_lock, activeflags);
worker->active = NULL;
spin_unlock_irqrestore(&worker->active_lock, activeflags);
-
+
continue;
}
@@ -369,10 +343,10 @@ static int qcnet_worker(void *arg)
usb_autopm_put_interface(worker->iface);
complete(&worker->work);
}
-
+
kfree(req);
- }
-
+ }
+
DBG("traffic thread exiting\n");
worker->thread = NULL;
return 0;
@@ -386,27 +360,27 @@ static int qcnet_startxmit(struct sk_buff *skb, struct net_device *netdev)
struct urbreq *req;
void *data;
struct usbnet *usbnet = netdev_priv(netdev);
-
+
DBG("\n");
-
+
if (!usbnet || !usbnet->net) {
DBG("failed to get usbnet device\n");
return NETDEV_TX_BUSY;
}
-
+
dev = (struct qcusbnet *)usbnet->data[0];
if (!dev) {
DBG("failed to get QMIDevice\n");
return NETDEV_TX_BUSY;
}
worker = &dev->worker;
-
+
if (qc_isdown(dev, DOWN_DRIVER_SUSPENDED)) {
DBG("device is suspended\n");
dump_stack();
return NETDEV_TX_BUSY;
}
-
+
req = kmalloc(sizeof(*req), GFP_ATOMIC);
if (!req) {
DBG("unable to allocate URBList memory\n");
@@ -431,8 +405,8 @@ static int qcnet_startxmit(struct sk_buff *skb, struct net_device *netdev)
memcpy(data, skb->data, skb->len);
usb_fill_bulk_urb(req->urb, dev->usbnet->udev, dev->usbnet->out,
- data, skb->len, qcnet_urbhook, worker);
-
+ data, skb->len, qcnet_urbhook, worker);
+
spin_lock_irqsave(&worker->urbs_lock, listflags);
list_add_tail(&req->node, &worker->urbs);
spin_unlock_irqrestore(&worker->urbs_lock, listflags);
@@ -450,12 +424,12 @@ static int qcnet_open(struct net_device *netdev)
int status = 0;
struct qcusbnet *dev;
struct usbnet *usbnet = netdev_priv(netdev);
-
+
if (!usbnet) {
DBG("failed to get usbnet device\n");
return -ENXIO;
}
-
+
dev = (struct qcusbnet *)usbnet->data[0];
if (!dev) {
DBG("failed to get QMIDevice\n");
@@ -470,30 +444,23 @@ static int qcnet_open(struct net_device *netdev)
spin_lock_init(&dev->worker.urbs_lock);
spin_lock_init(&dev->worker.active_lock);
init_completion(&dev->worker.work);
-
+
dev->worker.thread = kthread_run(qcnet_worker, &dev->worker, "qcnet_worker");
- if (IS_ERR(dev->worker.thread))
- {
+ if (IS_ERR(dev->worker.thread)) {
DBG("AutoPM thread creation error\n");
return PTR_ERR(dev->worker.thread);
}
qc_cleardown(dev, DOWN_NET_IFACE_STOPPED);
- if (dev->open)
- {
+ if (dev->open) {
status = dev->open(netdev);
- if (status == 0)
- {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
- usb_autopm_enable(dev->iface);
-#else
+ if (status == 0) {
usb_autopm_put_interface(dev->iface);
-#endif
}
} else {
DBG("no USBNetOpen defined\n");
}
-
+
return status;
}
@@ -506,7 +473,7 @@ int qcnet_stop(struct net_device *netdev)
DBG("failed to get netdevice\n");
return -ENXIO;
}
-
+
dev = (struct qcusbnet *)usbnet->data[0];
if (!dev) {
DBG("failed to get QMIDevice\n");
@@ -523,23 +490,21 @@ int qcnet_stop(struct net_device *netdev)
return 0;
}
-static const struct driver_info qc_netinfo =
-{
- .description = "QCUSBNet Ethernet Device",
- .flags = FLAG_ETHER,
- .bind = qcnet_bind,
- .unbind = qcnet_unbind,
- .data = 0,
+static const struct driver_info qc_netinfo = {
+ .description = "QCUSBNet Ethernet Device",
+ .flags = FLAG_ETHER,
+ .bind = qcnet_bind,
+ .unbind = qcnet_unbind,
+ .data = 0,
};
-#define MKVIDPID(v,p) \
- { \
- USB_DEVICE(v,p), \
- .driver_info = (unsigned long)&qc_netinfo, \
- }
+#define MKVIDPID(v, p) \
+{ \
+ USB_DEVICE(v, p), \
+ .driver_info = (unsigned long)&qc_netinfo, \
+}
-static const struct usb_device_id qc_vidpids [] =
-{
+static const struct usb_device_id qc_vidpids[] = {
MKVIDPID(0x05c6, 0x9215), /* Acer Gobi 2000 */
MKVIDPID(0x05c6, 0x9265), /* Asus Gobi 2000 */
MKVIDPID(0x16d8, 0x8002), /* CMOTech Gobi 2000 */
@@ -556,7 +521,7 @@ static const struct usb_device_id qc_vidpids [] =
MKVIDPID(0x1199, 0x9001), /* Sierra Wireless Gobi 2000 */
MKVIDPID(0x1199, 0x9002), /* Sierra Wireless Gobi 2000 */
MKVIDPID(0x1199, 0x9003), /* Sierra Wireless Gobi 2000 */
- MKVIDPID(0x1199, 0x9004), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9004), /* Sierra Wireless Gobi 2000 */
MKVIDPID(0x1199, 0x9005), /* Sierra Wireless Gobi 2000 */
MKVIDPID(0x1199, 0x9006), /* Sierra Wireless Gobi 2000 */
MKVIDPID(0x1199, 0x9007), /* Sierra Wireless Gobi 2000 */
@@ -576,9 +541,7 @@ int qcnet_probe(struct usb_interface *iface, const struct usb_device_id *vidpids
int status;
struct usbnet *usbnet;
struct qcusbnet *dev;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
struct net_device_ops *netdevops;
-#endif
status = usbnet_probe(iface, vidpids);
if (status < 0) {
@@ -586,11 +549,7 @@ int qcnet_probe(struct usb_interface *iface, const struct usb_device_id *vidpids
return status;
}
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
usbnet = usb_get_intfdata(iface);
-#else
- usbnet = iface->dev.platform_data;
-#endif
if (!usbnet || !usbnet->net) {
DBG("failed to get netdevice\n");
@@ -602,26 +561,18 @@ int qcnet_probe(struct usb_interface *iface, const struct usb_device_id *vidpids
DBG("falied to allocate device buffers");
return -ENOMEM;
}
-
+
usbnet->data[0] = (unsigned long)dev;
-
+
dev->usbnet = usbnet;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
- dev->open = usbnet->net->open;
- usbnet->net->open = qcnet_open;
- dev->stop = usbnet->net->stop;
- usbnet->net->stop = qcnet_stop;
- usbnet->net->hard_start_xmit = qcnet_startxmit;
- usbnet->net->tx_timeout = qcnet_txtimeout;
-#else
netdevops = kmalloc(sizeof(struct net_device_ops), GFP_KERNEL);
if (!netdevops) {
DBG("falied to allocate net device ops");
return -ENOMEM;
}
memcpy(netdevops, usbnet->net->netdev_ops, sizeof(struct net_device_ops));
-
+
dev->open = netdevops->ndo_open;
netdevops->ndo_open = qcnet_open;
dev->stop = netdevops->ndo_stop;
@@ -630,25 +581,19 @@ int qcnet_probe(struct usb_interface *iface, const struct usb_device_id *vidpids
netdevops->ndo_tx_timeout = qcnet_txtimeout;
usbnet->net->netdev_ops = netdevops;
-#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)
- memset(&(dev->usbnet->stats), 0, sizeof(struct net_device_stats));
-#else
memset(&(dev->usbnet->net->stats), 0, sizeof(struct net_device_stats));
-#endif
dev->iface = iface;
memset(&(dev->meid), '0', 14);
-
- DBG("Mac Address:\n");
- printhex(&dev->usbnet->net->dev_addr[0], 6);
+
+ DBG("Mac Address: %pM\n", dev->usbnet->net->dev_addr);
dev->valid = false;
memset(&dev->qmi, 0, sizeof(struct qmidev));
dev->qmi.devclass = devclass;
-
+
INIT_LIST_HEAD(&dev->qmi.clients);
init_completion(&dev->worker.work);
spin_lock_init(&dev->qmi.clients_lock);
@@ -661,14 +606,12 @@ int qcnet_probe(struct usb_interface *iface, const struct usb_device_id *vidpids
if (status) {
qc_deregister(dev);
}
-
+
return status;
}
-
EXPORT_SYMBOL_GPL(qcnet_probe);
-static struct usb_driver qcusbnet =
-{
+static struct usb_driver qcusbnet = {
.name = "QCUSBNet2k",
.id_table = qc_vidpids,
.probe = qcnet_probe,
diff --git a/drivers/net/usb/qcusbnet/qmi.c b/drivers/net/usb/qcusbnet/qmi.c
index 2fc8ce8..cdbdbaf 100755
--- a/drivers/net/usb/qcusbnet/qmi.c
+++ b/drivers/net/usb/qcusbnet/qmi.c
@@ -177,7 +177,7 @@ void *qmidms_new_getmeid(u8 tid, size_t *size)
int qmux_parse(u16 *cid, void *buf, size_t size)
{
struct qmux *qmux = buf;
-
+
if (!buf || size < 12)
return -ENOMEM;
@@ -203,13 +203,14 @@ int qmux_fill(u16 cid, void *buf, size_t size)
return 0;
}
-static u16 tlv_get(void *msg, u16 msgsize, u8 type, void *buf, u16 bufsize) {
+static u16 tlv_get(void *msg, u16 msgsize, u8 type, void *buf, u16 bufsize)
+{
u16 pos;
u16 msize = 0;
-
+
if (!msg || !buf)
return -ENOMEM;
-
+
for (pos = 4; pos + 3 < msgsize; pos += msize + 3) {
msize = *(u16 *)(msg + pos + 1);
if (*(u8 *)(msg + pos) == type) {
@@ -220,7 +221,7 @@ static u16 tlv_get(void *msg, u16 msgsize, u8 type, void *buf, u16 bufsize) {
return msize;
}
}
-
+
return -ENOMSG;
}
@@ -305,21 +306,21 @@ int qmiwds_event_resp(void *buf, u16 size, struct qmiwds_stats *stats)
result = qmi_msgid(buf, size);
if (result == 0x01) {
- tlv_get(buf, size, 0x10, (void*)&stats->txok, 4);
- tlv_get(buf, size, 0x11, (void*)&stats->rxok, 4);
- tlv_get(buf, size, 0x12, (void*)&stats->txerr, 4);
- tlv_get(buf, size, 0x13, (void*)&stats->rxerr, 4);
- tlv_get(buf, size, 0x14, (void*)&stats->txofl, 4);
- tlv_get(buf, size, 0x15, (void*)&stats->rxofl, 4);
- tlv_get(buf, size, 0x19, (void*)&stats->txbytesok, 8);
- tlv_get(buf, size, 0x1A, (void*)&stats->rxbytesok, 8);
+ tlv_get(buf, size, 0x10, &stats->txok, 4);
+ tlv_get(buf, size, 0x11, &stats->rxok, 4);
+ tlv_get(buf, size, 0x12, &stats->txerr, 4);
+ tlv_get(buf, size, 0x13, &stats->rxerr, 4);
+ tlv_get(buf, size, 0x14, &stats->txofl, 4);
+ tlv_get(buf, size, 0x15, &stats->rxofl, 4);
+ tlv_get(buf, size, 0x19, &stats->txbytesok, 8);
+ tlv_get(buf, size, 0x1A, &stats->rxbytesok, 8);
} else if (result == 0x22) {
result = tlv_get(buf, size, 0x01, &status[0], 2);
if (result >= 1)
stats->linkstate = status[0] == 0x02;
if (result == 2)
stats->reconfigure = status[1] == 0x01;
-
+
if (result < 0)
return result;
} else {
@@ -349,10 +350,9 @@ int qmidms_meid_resp(void *buf, u16 size, char *meid, int meidsize)
if (result)
return -EFAULT;
- result = tlv_get(buf, size, 0x12, (void*)meid, 14);
+ result = tlv_get(buf, size, 0x12, meid, 14);
if (result != 14)
return -EFAULT;
return 0;
}
-
diff --git a/drivers/net/usb/qcusbnet/qmidevice.c b/drivers/net/usb/qcusbnet/qmidevice.c
index b5006be..da7e42d 100755
--- a/drivers/net/usb/qcusbnet/qmidevice.c
+++ b/drivers/net/usb/qcusbnet/qmidevice.c
@@ -28,7 +28,7 @@ struct readreq {
struct notifyreq {
struct list_head node;
- void (* func)(struct qcusbnet *, u16, void *);
+ void (*func)(struct qcusbnet *, u16, void *);
u16 tid;
void *data;
};
@@ -55,15 +55,15 @@ struct qmihandle {
};
extern int debug;
-static int qcusbnet2k_fwdelay = 0;
+static int qcusbnet2k_fwdelay;
static bool device_valid(struct qcusbnet *dev);
static struct client *client_bycid(struct qcusbnet *dev, u16 cid);
static bool client_addread(struct qcusbnet *dev, u16 cid, u16 tid, void *data, u16 size);
static bool client_delread(struct qcusbnet *dev, u16 cid, u16 tid, void **data, u16 *size);
static bool client_addnotify(struct qcusbnet *dev, u16 cid, u16 tid,
- void (*hook)(struct qcusbnet *, u16 cid, void *),
- void *data);
+ void (*hook)(struct qcusbnet *, u16 cid, void *),
+ void *data);
static bool client_notify(struct qcusbnet *dev, u16 cid, u16 tid);
static bool client_addurb(struct qcusbnet *dev, u16 cid, struct urb *urb);
static struct urb *client_delurb(struct qcusbnet *dev, u16 cid);
@@ -72,23 +72,22 @@ static int devqmi_open(struct inode *inode, struct file *file);
static long devqmi_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
static int devqmi_close(struct file *file, fl_owner_t ftable);
static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size,
- loff_t *pos);
+ loff_t *pos);
static ssize_t devqmi_write(struct file *file, const char __user *buf,
- size_t size, loff_t *pos);
+ size_t size, loff_t *pos);
static bool qmi_ready(struct qcusbnet *dev, u16 timeout);
static void wds_callback(struct qcusbnet *dev, u16 cid, void *data);
static int setup_wds_callback(struct qcusbnet *dev);
static int qmidms_getmeid(struct qcusbnet *dev);
-#define IOCTL_QMI_GET_SERVICE_FILE 0x8BE0 + 1
-#define IOCTL_QMI_GET_DEVICE_VIDPID 0x8BE0 + 2
-#define IOCTL_QMI_GET_DEVICE_MEID 0x8BE0 + 3
-#define CDC_GET_ENCAPSULATED_RESPONSE 0x01A1ll
-#define CDC_CONNECTION_SPEED_CHANGE 0x08000000002AA1ll
+#define IOCTL_QMI_GET_SERVICE_FILE (0x8BE0 + 1)
+#define IOCTL_QMI_GET_DEVICE_VIDPID (0x8BE0 + 2)
+#define IOCTL_QMI_GET_DEVICE_MEID (0x8BE0 + 3)
+#define CDC_GET_ENCAPSULATED_RESPONSE 0x01A1ll
+#define CDC_CONNECTION_SPEED_CHANGE 0x08000000002AA1ll
-struct file_operations devqmi_fops =
-{
+static const struct file_operations devqmi_fops = {
.owner = THIS_MODULE,
.read = devqmi_read,
.write = devqmi_write,
@@ -112,7 +111,7 @@ static inline void assert_locked(struct qcusbnet *dev)
static bool device_valid(struct qcusbnet *dev)
{
return dev && dev->valid;
-}
+}
void printhex(const void *data, size_t size)
{
@@ -132,7 +131,7 @@ void printhex(const void *data, size_t size)
snprintf(buf + (pos * 3), 4, "%02X ", cdata[pos]);
}
- DBG( " : %s\n", buf);
+ DBG(" : %s\n", buf);
kfree(buf);
}
@@ -203,9 +202,9 @@ static void read_callback(struct urb *urb)
}
if (cid == QMICTL)
- tid = *(u8*)(data + result + 1);
+ tid = *(u8 *)(data + result + 1);
else
- tid = *(u16*)(data + result + 1);
+ tid = *(u16 *)(data + result + 1);
spin_lock_irqsave(&dev->qmi.clients_lock, flags);
list_for_each(node, &dev->qmi.clients) {
@@ -230,7 +229,7 @@ static void read_callback(struct urb *urb)
break;
}
}
-
+
spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
}
@@ -250,25 +249,25 @@ static void int_callback(struct urb *urb)
if (urb->status != -EOVERFLOW)
return;
} else {
- if ((urb->actual_length == 8)
- && (*(u64*)urb->transfer_buffer == CDC_GET_ENCAPSULATED_RESPONSE)) {
+ if ((urb->actual_length == 8) &&
+ (*(u64 *)urb->transfer_buffer == CDC_GET_ENCAPSULATED_RESPONSE)) {
usb_fill_control_urb(dev->qmi.readurb, dev->usbnet->udev,
- usb_rcvctrlpipe(dev->usbnet->udev, 0),
- (unsigned char *)dev->qmi.readsetup,
- dev->qmi.readbuf,
- DEFAULT_READ_URB_LENGTH,
- read_callback, dev);
+ usb_rcvctrlpipe(dev->usbnet->udev, 0),
+ (unsigned char *)dev->qmi.readsetup,
+ dev->qmi.readbuf,
+ DEFAULT_READ_URB_LENGTH,
+ read_callback, dev);
status = usb_submit_urb(dev->qmi.readurb, GFP_ATOMIC);
if (status) {
DBG("Error submitting Read URB %d\n", status);
return;
}
- } else if ((urb->actual_length == 16)
- && (*(u64*)urb->transfer_buffer == CDC_CONNECTION_SPEED_CHANGE)) {
+ } else if ((urb->actual_length == 16) &&
+ (*(u64 *)urb->transfer_buffer == CDC_CONNECTION_SPEED_CHANGE)) {
/* if upstream or downstream is 0, stop traffic.
* Otherwise resume it */
- if ((*(u32*)(urb->transfer_buffer + 8) == 0)
- || (*(u32*)(urb->transfer_buffer + 12) == 0)) {
+ if ((*(u32 *)(urb->transfer_buffer + 8) == 0) ||
+ (*(u32 *)(urb->transfer_buffer + 12) == 0)) {
qc_setdown(dev, DOWN_CDC_CONNECTION_SPEED);
DBG("traffic stopping due to CONNECTION_SPEED_CHANGE\n");
} else {
@@ -284,8 +283,8 @@ static void int_callback(struct urb *urb)
interval = (dev->usbnet->udev->speed == USB_SPEED_HIGH) ? 7 : 3;
usb_fill_int_urb(urb, urb->dev, urb->pipe, urb->transfer_buffer,
- urb->transfer_buffer_length, urb->complete,
- urb->context, interval);
+ urb->transfer_buffer_length, urb->complete,
+ urb->context, interval);
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status)
DBG("Error re-submitting Int URB %d\n", status);
@@ -300,13 +299,13 @@ int qc_startread(struct qcusbnet *dev)
DBG("Invalid device!\n");
return -ENXIO;
}
-
+
dev->qmi.readurb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->qmi.readurb) {
DBG("Error allocating read urb\n");
return -ENOMEM;
}
-
+
dev->qmi.inturb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->qmi.inturb) {
usb_free_urb(dev->qmi.readurb);
@@ -321,7 +320,7 @@ int qc_startread(struct qcusbnet *dev)
DBG("Error allocating read buffer\n");
return -ENOMEM;
}
-
+
dev->qmi.intbuf = kmalloc(DEFAULT_READ_URB_LENGTH, GFP_KERNEL);
if (!dev->qmi.intbuf) {
usb_free_urb(dev->qmi.readurb);
@@ -329,8 +328,8 @@ int qc_startread(struct qcusbnet *dev)
kfree(dev->qmi.readbuf);
DBG("Error allocating int buffer\n");
return -ENOMEM;
- }
-
+ }
+
dev->qmi.readsetup = kmalloc(sizeof(*dev->qmi.readsetup), GFP_KERNEL);
if (!dev->qmi.readsetup) {
usb_free_urb(dev->qmi.readurb);
@@ -348,15 +347,15 @@ int qc_startread(struct qcusbnet *dev)
dev->qmi.readsetup->len = DEFAULT_READ_URB_LENGTH;
interval = (dev->usbnet->udev->speed == USB_SPEED_HIGH) ? 7 : 3;
-
+
usb_fill_int_urb(dev->qmi.inturb, dev->usbnet->udev,
- usb_rcvintpipe(dev->usbnet->udev, 0x81),
- dev->qmi.intbuf, DEFAULT_READ_URB_LENGTH,
- int_callback, dev, interval);
+ usb_rcvintpipe(dev->usbnet->udev, 0x81),
+ dev->qmi.intbuf, DEFAULT_READ_URB_LENGTH,
+ int_callback, dev, interval);
return usb_submit_urb(dev->qmi.inturb, GFP_KERNEL);
}
-void qc_stopread(struct qcusbnet * dev)
+void qc_stopread(struct qcusbnet *dev)
{
if (dev->qmi.readurb) {
DBG("Killng read URB\n");
@@ -374,7 +373,7 @@ void qc_stopread(struct qcusbnet * dev)
dev->qmi.readbuf = NULL;
kfree(dev->qmi.intbuf);
dev->qmi.intbuf = NULL;
-
+
usb_free_urb(dev->qmi.readurb);
dev->qmi.readurb = NULL;
usb_free_urb(dev->qmi.inturb);
@@ -382,12 +381,13 @@ void qc_stopread(struct qcusbnet * dev)
}
static int read_async(struct qcusbnet *dev, u16 cid, u16 tid,
- void (*hook)(struct qcusbnet*, u16, void *), void *data)
+ void (*hook)(struct qcusbnet *, u16, void *),
+ void *data)
{
struct list_head *node;
struct client *client;
struct readreq *readreq;
-
+
unsigned long flags;
if (!device_valid(dev)) {
@@ -420,7 +420,7 @@ static int read_async(struct qcusbnet *dev, u16 cid, u16 tid,
return 0;
}
-static void upsem(struct qcusbnet *dev, u16 cid, void *data)
+static void upsem(struct qcusbnet *dev, u16 cid, void *data)
{
DBG("0x%04X\n", cid);
up((struct semaphore *)data);
@@ -430,10 +430,10 @@ static int read_sync(struct qcusbnet *dev, void **buf, u16 cid, u16 tid)
{
struct list_head *node;
int result;
- struct client * client;
+ struct client *client;
struct notifyreq *notify;
struct semaphore sem;
- void * data;
+ void *data;
unsigned long flags;
u16 size;
@@ -441,7 +441,7 @@ static int read_sync(struct qcusbnet *dev, void **buf, u16 cid, u16 tid)
DBG("Invalid device!\n");
return -ENXIO;
}
-
+
spin_lock_irqsave(&dev->qmi.clients_lock, flags);
client = client_bycid(dev, cid);
@@ -450,7 +450,7 @@ static int read_sync(struct qcusbnet *dev, void **buf, u16 cid, u16 tid)
spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
return -ENXIO;
}
-
+
while (!client_delread(dev, cid, tid, &data, &size)) {
sema_init(&sem, 0);
if (!client_addnotify(dev, cid, tid, upsem, &sem)) {
@@ -477,15 +477,15 @@ static int read_sync(struct qcusbnet *dev, void **buf, u16 cid, u16 tid)
spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
return -EINTR;
}
-
+
if (!device_valid(dev)) {
DBG("Invalid device!\n");
return -ENXIO;
}
-
+
spin_lock_irqsave(&dev->qmi.clients_lock, flags);
}
-
+
spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
*buf = data;
return size;
@@ -536,25 +536,22 @@ static int write_sync(struct qcusbnet *dev, char *buf, int size, u16 cid)
setup.len = size;
usb_fill_control_urb(urb, dev->usbnet->udev,
- usb_sndctrlpipe(dev->usbnet->udev, 0),
- (unsigned char *)&setup, (void*)buf, size,
- NULL, dev);
+ usb_sndctrlpipe(dev->usbnet->udev, 0),
+ (unsigned char *)&setup, (void *)buf, size,
+ NULL, dev);
DBG("Actual Write:\n");
printhex(buf, size);
sema_init(&sem, 0);
-
+
urb->complete = write_callback;
urb->context = &sem;
-
+
result = usb_autopm_get_interface(dev->iface);
if (result < 0) {
DBG("unable to resume interface: %d\n", result);
if (result == -EPERM) {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
- dev->usbnet->udev->auto_pm = 0;
-#endif
qc_suspend(dev->iface, PMSG_SUSPEND);
}
return result;
@@ -564,7 +561,7 @@ static int write_sync(struct qcusbnet *dev, char *buf, int size, u16 cid)
if (!client_addurb(dev, cid, urb)) {
usb_free_urb(urb);
- spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
usb_autopm_put_interface(dev->iface);
return -EINVAL;
}
@@ -582,8 +579,8 @@ static int write_sync(struct qcusbnet *dev, char *buf, int size, u16 cid)
usb_autopm_put_interface(dev->iface);
return result;
}
-
- spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
result = down_interruptible(&sem);
if (!device_valid(dev)) {
DBG("Invalid device!\n");
@@ -592,13 +589,12 @@ static int write_sync(struct qcusbnet *dev, char *buf, int size, u16 cid)
usb_autopm_put_interface(dev->iface);
spin_lock_irqsave(&dev->qmi.clients_lock, flags);
- if (client_delurb(dev, cid) != urb)
- {
+ if (client_delurb(dev, cid) != urb) {
DBG("Didn't get write URB back\n");
- spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
return -EINVAL;
}
- spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
if (!result) {
if (!urb->status) {
@@ -622,13 +618,13 @@ static int client_alloc(struct qcusbnet *dev, u8 type)
u16 cid;
struct client *client;
int result;
- void * wbuf;
+ void *wbuf;
size_t wbufsize;
- void * rbuf;
+ void *rbuf;
u16 rbufsize;
unsigned long flags;
u8 tid;
-
+
if (!device_valid(dev)) {
DBG("Invalid device!\n");
return -ENXIO;
@@ -691,12 +687,12 @@ static void client_free(struct qcusbnet *dev, u16 cid)
struct list_head *node, *tmp;
int result;
struct client *client;
- struct urb * urb;
- void * data;
+ struct urb *urb;
+ void *data;
u16 size;
- void * wbuf;
+ void *wbuf;
size_t wbufsize;
- void * rbuf;
+ void *rbuf;
u16 rbufsize;
unsigned long flags;
u8 tid;
@@ -705,11 +701,10 @@ static void client_free(struct qcusbnet *dev, u16 cid)
DBG("invalid device\n");
return;
}
-
+
DBG("releasing 0x%04X\n", cid);
- if (cid != QMICTL)
- {
+ if (cid != QMICTL) {
tid = atomic_add_return(1, &dev->qmi.qmitid);
if (!tid)
tid = atomic_add_return(1, &dev->qmi.qmitid);
@@ -741,7 +736,9 @@ static void client_free(struct qcusbnet *dev, u16 cid)
list_for_each_safe(node, tmp, &dev->qmi.clients) {
client = list_entry(node, struct client, node);
if (client->cid == cid) {
- while (client_notify(dev, cid, 0));
+ while (client_notify(dev, cid, 0)) {
+ ;
+ }
urb = client_delurb(dev, cid);
while (urb != NULL) {
@@ -757,7 +754,7 @@ static void client_free(struct qcusbnet *dev, u16 cid)
kfree(client);
}
}
-
+
spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
}
@@ -765,14 +762,14 @@ struct client *client_bycid(struct qcusbnet *dev, u16 cid)
{
struct list_head *node;
struct client *client;
-
+
if (!device_valid(dev)) {
DBG("Invalid device\n");
return NULL;
}
- assert_locked(dev);
-
+ assert_locked(dev);
+
list_for_each(node, &dev->qmi.clients) {
client = list_entry(node, struct client, node);
if (client->cid == cid)
@@ -784,7 +781,7 @@ struct client *client_bycid(struct qcusbnet *dev, u16 cid)
}
static bool client_addread(struct qcusbnet *dev, u16 cid, u16 tid, void *data,
- u16 size)
+ u16 size)
{
struct client *client;
struct readreq *req;
@@ -808,12 +805,12 @@ static bool client_addread(struct qcusbnet *dev, u16 cid, u16 tid, void *data,
req->tid = tid;
list_add_tail(&req->node, &client->reads);
-
+
return true;
}
static bool client_delread(struct qcusbnet *dev, u16 cid, u16 tid, void **data,
- u16 *size)
+ u16 *size)
{
struct client *client;
struct readreq *req;
@@ -826,7 +823,7 @@ static bool client_delread(struct qcusbnet *dev, u16 cid, u16 tid, void **data,
DBG("Could not find this client's memory 0x%04X\n", cid);
return false;
}
-
+
list_for_each(node, &client->reads) {
req = list_entry(node, struct readreq, node);
if (!tid || tid == req->tid) {
@@ -836,17 +833,17 @@ static bool client_delread(struct qcusbnet *dev, u16 cid, u16 tid, void **data,
kfree(req);
return true;
}
-
+
DBG("skipping 0x%04X data TID = %x\n", cid, req->tid);
}
-
+
DBG("No read memory to pop, Client 0x%04X, TID = %x\n", cid, tid);
return false;
}
static bool client_addnotify(struct qcusbnet *dev, u16 cid, u16 tid,
- void (*hook)(struct qcusbnet *, u16, void *),
- void *data)
+ void (*hook)(struct qcusbnet *, u16, void *),
+ void *data)
{
struct client *client;
struct notifyreq *req;
@@ -895,10 +892,10 @@ static bool client_notify(struct qcusbnet *dev, u16 cid, u16 tid)
delnotify = notify;
break;
}
-
+
DBG("skipping data TID = %x\n", notify->tid);
}
-
+
if (delnotify) {
list_del(&delnotify->node);
if (delnotify->func) {
@@ -910,7 +907,7 @@ static bool client_notify(struct qcusbnet *dev, u16 cid, u16 tid)
return true;
}
- DBG("no one to notify for TID %x\n", tid);
+ DBG("no one to notify for TID %x\n", tid);
return false;
}
@@ -931,11 +928,11 @@ static bool client_addurb(struct qcusbnet *dev, u16 cid, struct urb *urb)
if (!req) {
DBG("Mem error\n");
return false;
- }
+ }
req->urb = urb;
list_add_tail(&req->node, &client->urbs);
-
+
return true;
}
@@ -967,9 +964,9 @@ static struct urb *client_delurb(struct qcusbnet *dev, u16 cid)
static int devqmi_open(struct inode *inode, struct file *file)
{
- struct qmihandle * handle;
+ struct qmihandle *handle;
struct qmidev *qmidev = container_of(inode->i_cdev, struct qmidev, cdev);
- struct qcusbnet * dev = container_of(qmidev, struct qcusbnet, qmi);
+ struct qcusbnet *dev = container_of(qmidev, struct qcusbnet, qmi);
if (!device_valid(dev)) {
DBG("Invalid device\n");
@@ -981,11 +978,11 @@ static int devqmi_open(struct inode *inode, struct file *file)
DBG("Mem error\n");
return -ENOMEM;
}
-
+
handle = (struct qmihandle *)file->private_data;
handle->cid = (u16)-1;
handle->dev = dev;
-
+
return 0;
}
@@ -993,14 +990,14 @@ static long devqmi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int result;
u32 vidpid;
-
+
struct qmihandle *handle = (struct qmihandle *)file->private_data;
if (!handle) {
DBG("Bad file data\n");
return -EBADF;
}
-
+
if (!device_valid(handle->dev)) {
DBG("Invalid device! Updating f_ops\n");
file->f_op = file->f_dentry->d_inode->i_fop;
@@ -1008,77 +1005,77 @@ static long devqmi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
switch (cmd) {
- case IOCTL_QMI_GET_SERVICE_FILE:
+ case IOCTL_QMI_GET_SERVICE_FILE:
- DBG("Setting up QMI for service %lu\n", arg);
- if (!(u8)arg) {
- DBG("Cannot use QMICTL from userspace\n");
- return -EINVAL;
- }
+ DBG("Setting up QMI for service %lu\n", arg);
+ if (!(u8)arg) {
+ DBG("Cannot use QMICTL from userspace\n");
+ return -EINVAL;
+ }
- if (handle->cid != (u16)-1) {
- DBG("Close the current connection before opening a new one\n");
- return -EBADR;
- }
-
- result = client_alloc(handle->dev, (u8)arg);
- if (result < 0)
- return result;
- handle->cid = result;
+ if (handle->cid != (u16)-1) {
+ DBG("Close the current connection before opening a new one\n");
+ return -EBADR;
+ }
- return 0;
- break;
+ result = client_alloc(handle->dev, (u8)arg);
+ if (result < 0)
+ return result;
+ handle->cid = result;
+ return 0;
+ break;
- case IOCTL_QMI_GET_DEVICE_VIDPID:
- if (!arg) {
- DBG("Bad VIDPID buffer\n");
- return -EINVAL;
- }
-
- if (!handle->dev->usbnet) {
- DBG("Bad usbnet\n");
- return -ENOMEM;
- }
- if (!handle->dev->usbnet->udev) {
- DBG("Bad udev\n");
- return -ENOMEM;
- }
+ case IOCTL_QMI_GET_DEVICE_VIDPID:
+ if (!arg) {
+ DBG("Bad VIDPID buffer\n");
+ return -EINVAL;
+ }
- vidpid = ((le16_to_cpu(handle->dev->usbnet->udev->descriptor.idVendor) << 16)
- + le16_to_cpu(handle->dev->usbnet->udev->descriptor.idProduct));
+ if (!handle->dev->usbnet) {
+ DBG("Bad usbnet\n");
+ return -ENOMEM;
+ }
- result = copy_to_user((unsigned int *)arg, &vidpid, 4);
- if (result)
- DBG("Copy to userspace failure\n");
+ if (!handle->dev->usbnet->udev) {
+ DBG("Bad udev\n");
+ return -ENOMEM;
+ }
- return result;
- break;
+ vidpid = ((le16_to_cpu(handle->dev->usbnet->udev->descriptor.idVendor) << 16)
+ + le16_to_cpu(handle->dev->usbnet->udev->descriptor.idProduct));
- case IOCTL_QMI_GET_DEVICE_MEID:
- if (!arg) {
- DBG("Bad MEID buffer\n");
- return -EINVAL;
- }
-
- result = copy_to_user((unsigned int *)arg, &handle->dev->meid[0], 14);
- if (result)
- DBG("copy to userspace failure\n");
+ result = copy_to_user((unsigned int *)arg, &vidpid, 4);
+ if (result)
+ DBG("Copy to userspace failure\n");
- return result;
- break;
- default:
- return -EBADRQC;
+ return result;
+ break;
+
+ case IOCTL_QMI_GET_DEVICE_MEID:
+ if (!arg) {
+ DBG("Bad MEID buffer\n");
+ return -EINVAL;
+ }
+
+ result = copy_to_user((unsigned int *)arg, &handle->dev->meid[0], 14);
+ if (result)
+ DBG("copy to userspace failure\n");
+
+ return result;
+ break;
+ default:
+ return -EBADRQC;
}
}
static int devqmi_close(struct file *file, fl_owner_t ftable)
{
- struct qmihandle * handle = (struct qmihandle *)file->private_data;
- struct list_head * tasks;
- struct task_struct * task;
- struct fdtable * fdtable;
+ struct qmihandle *handle = (struct qmihandle *)file->private_data;
+ struct list_head *tasks;
+ struct task_struct *task;
+ struct fdtable *fdtable;
int count = 0;
int used = 0;
unsigned long flags;
@@ -1109,7 +1106,7 @@ static int devqmi_close(struct file *file, fl_owner_t ftable)
}
spin_unlock_irqrestore(&task->files->file_lock, flags);
}
-
+
if (used > 0) {
DBG("not closing, as this FD is open by %d other process\n", used);
return 0;
@@ -1121,9 +1118,9 @@ static int devqmi_close(struct file *file, fl_owner_t ftable)
file->f_op = file->f_dentry->d_inode->i_fop;
return -ENXIO;
}
-
+
DBG("0x%04X\n", handle->cid);
-
+
file->private_data = NULL;
if (handle->cid != (u16)-1)
@@ -1134,12 +1131,12 @@ static int devqmi_close(struct file *file, fl_owner_t ftable)
}
static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size,
- loff_t *pos)
+ loff_t *pos)
{
int result;
- void * data = NULL;
- void * smalldata;
- struct qmihandle * handle = (struct qmihandle *)file->private_data;
+ void *data = NULL;
+ void *smalldata;
+ struct qmihandle *handle = (struct qmihandle *)file->private_data;
if (!handle) {
DBG("Bad file data\n");
@@ -1151,17 +1148,17 @@ static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size,
file->f_op = file->f_dentry->d_inode->i_fop;
return -ENXIO;
}
-
+
if (handle->cid == (u16)-1) {
DBG("Client ID must be set before reading 0x%04X\n",
handle->cid);
return -EBADR;
}
-
+
result = read_sync(handle->dev, &data, handle->cid, 0);
if (result <= 0)
return result;
-
+
result -= qmux_size;
smalldata = data + qmux_size;
@@ -1175,13 +1172,13 @@ static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size,
DBG("Error copying read data to user\n");
result = -EFAULT;
}
-
+
kfree(data);
return result;
}
-static ssize_t devqmi_write (struct file *file, const char __user * buf,
- size_t size, loff_t *pos)
+static ssize_t devqmi_write(struct file *file, const char __user * buf,
+ size_t size, loff_t *pos)
{
int status;
void *wbuf;
@@ -1203,7 +1200,7 @@ static ssize_t devqmi_write (struct file *file, const char __user * buf,
handle->cid);
return -EBADR;
}
-
+
wbuf = kmalloc(size + qmux_size, GFP_KERNEL);
if (!wbuf)
return -ENOMEM;
@@ -1215,7 +1212,7 @@ static ssize_t devqmi_write (struct file *file, const char __user * buf,
}
status = write_sync(handle->dev, wbuf, size + qmux_size,
- handle->cid);
+ handle->cid);
kfree(wbuf);
if (status == size + qmux_size)
@@ -1227,9 +1224,9 @@ int qc_register(struct qcusbnet *dev)
{
int result;
int qmiidx = 0;
- dev_t devno;
- char * name;
-
+ dev_t devno;
+ char *name;
+
dev->valid = true;
result = client_alloc(dev, QMICTL);
if (result) {
@@ -1243,7 +1240,7 @@ int qc_register(struct qcusbnet *dev)
dev->valid = false;
return result;
}
-
+
if (!qmi_ready(dev, 30000)) {
DBG("Device unresponsive to QMI\n");
return -ETIMEDOUT;
@@ -1288,12 +1285,8 @@ int qc_register(struct qcusbnet *dev)
}
printk(KERN_INFO "creating qcqmi%d\n", qmiidx);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
device_create(dev->qmi.devclass, NULL, devno, NULL, "qcqmi%d", qmiidx);
-#else
- device_create(dev->qmi.devclass, NULL, devno, "qcqmi%d", qmiidx);
-#endif
-
+
dev->qmi.devnum = devno;
return 0;
}
@@ -1302,12 +1295,12 @@ void qc_deregister(struct qcusbnet *dev)
{
struct list_head *node;
struct client *client;
- struct inode * inode;
- struct list_head * inodes;
- struct list_head * tasks;
- struct task_struct * task;
- struct fdtable * fdtable;
- struct file * file;
+ struct inode *inode;
+ struct list_head *inodes;
+ struct list_head *tasks;
+ struct task_struct *task;
+ struct fdtable *fdtable;
+ struct file *file;
unsigned long flags;
int count = 0;
@@ -1337,7 +1330,7 @@ void qc_deregister(struct qcusbnet *dev)
file = fdtable->fd[count];
if (file != NULL && file->f_dentry != NULL) {
if (file->f_dentry->d_inode == inode) {
- rcu_assign_pointer(fdtable->fd[count], NULL);
+ rcu_assign_pointer(fdtable->fd[count], NULL);
spin_unlock_irqrestore(&task->files->file_lock, flags);
DBG("forcing close of open file handle\n");
filp_close(file, task->files);
@@ -1359,15 +1352,15 @@ void qc_deregister(struct qcusbnet *dev)
static bool qmi_ready(struct qcusbnet *dev, u16 timeout)
{
int result;
- void * wbuf;
+ void *wbuf;
size_t wbufsize;
- void * rbuf;
+ void *rbuf;
u16 rbufsize;
struct semaphore sem;
u16 now;
unsigned long flags;
u8 tid;
-
+
if (!device_valid(dev)) {
DBG("Invalid device\n");
return -EFAULT;
@@ -1380,12 +1373,11 @@ static bool qmi_ready(struct qcusbnet *dev, u16 timeout)
tid = atomic_add_return(1, &dev->qmi.qmitid);
if (!tid)
tid = atomic_add_return(1, &dev->qmi.qmitid);
- if (wbuf)
- kfree(wbuf);
+ kfree(wbuf);
wbuf = qmictl_new_ready(tid, &wbufsize);
if (!wbuf)
return -ENOMEM;
-
+
result = read_async(dev, QMICTL, tid, upsem, &sem);
if (result) {
kfree(wbuf);
@@ -1409,12 +1401,11 @@ static bool qmi_ready(struct qcusbnet *dev, u16 timeout)
}
}
- if (wbuf)
- kfree(wbuf);
+ kfree(wbuf);
if (now >= timeout)
return false;
-
+
DBG("QMI Ready after %u milliseconds\n", now);
/* 3580 and newer doesn't need a delay; older needs 5000ms */
@@ -1428,14 +1419,10 @@ static void wds_callback(struct qcusbnet *dev, u16 cid, void *data)
{
bool ret;
int result;
- void * rbuf;
+ void *rbuf;
u16 rbufsize;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)
- struct net_device_stats * stats = &(dev->usbnet->stats);
-#else
- struct net_device_stats * stats = &(dev->usbnet->net->stats);
-#endif
+ struct net_device_stats *stats = &(dev->usbnet->net->stats);
struct qmiwds_stats dstats = {
.txok = (u32)-1,
@@ -1448,7 +1435,7 @@ static void wds_callback(struct qcusbnet *dev, u16 cid, void *data)
.rxbytesok = (u64)-1,
};
unsigned long flags;
-
+
if (!device_valid(dev)) {
DBG("Invalid device\n");
return;
@@ -1456,13 +1443,13 @@ static void wds_callback(struct qcusbnet *dev, u16 cid, void *data)
spin_lock_irqsave(&dev->qmi.clients_lock, flags);
ret = client_delread(dev, cid, 0, &rbuf, &rbufsize);
- spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
-
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+
if (!ret) {
DBG("WDS callback failed to get data\n");
return;
}
-
+
dstats.linkstate = !qc_isdown(dev, DOWN_NO_NDIS_CONNECTION);
dstats.reconfigure = false;
@@ -1472,25 +1459,25 @@ static void wds_callback(struct qcusbnet *dev, u16 cid, void *data)
} else {
if (dstats.txofl != (u32)-1)
stats->tx_fifo_errors = dstats.txofl;
-
+
if (dstats.rxofl != (u32)-1)
stats->rx_fifo_errors = dstats.rxofl;
if (dstats.txerr != (u32)-1)
stats->tx_errors = dstats.txerr;
-
+
if (dstats.rxerr != (u32)-1)
stats->rx_errors = dstats.rxerr;
if (dstats.txok != (u32)-1)
stats->tx_packets = dstats.txok + stats->tx_errors;
-
+
if (dstats.rxok != (u32)-1)
stats->rx_packets = dstats.rxok + stats->rx_errors;
if (dstats.txbytesok != (u64)-1)
stats->tx_bytes = dstats.txbytesok;
-
+
if (dstats.rxbytesok != (u64)-1)
stats->rx_bytes = dstats.rxbytesok;
@@ -1516,7 +1503,7 @@ static void wds_callback(struct qcusbnet *dev, u16 cid, void *data)
DBG("unable to setup next async read\n");
}
-static int setup_wds_callback(struct qcusbnet * dev)
+static int setup_wds_callback(struct qcusbnet *dev)
{
int result;
void *buf;
@@ -1527,7 +1514,7 @@ static int setup_wds_callback(struct qcusbnet * dev)
DBG("Invalid device\n");
return -EFAULT;
}
-
+
result = client_alloc(dev, QMIWDS);
if (result < 0)
return result;
@@ -1561,8 +1548,8 @@ static int setup_wds_callback(struct qcusbnet * dev)
}
result = usb_control_msg(dev->usbnet->udev,
- usb_sndctrlpipe(dev->usbnet->udev, 0),
- 0x22, 0x21, 1, 0, NULL, 0, 100);
+ usb_sndctrlpipe(dev->usbnet->udev, 0),
+ 0x22, 0x21, 1, 0, NULL, 0, 100);
if (result < 0) {
DBG("Bad SetControlLineState status %d\n", result);
return result;
@@ -1571,12 +1558,12 @@ static int setup_wds_callback(struct qcusbnet * dev)
return 0;
}
-static int qmidms_getmeid(struct qcusbnet * dev)
+static int qmidms_getmeid(struct qcusbnet *dev)
{
int result;
- void * wbuf;
+ void *wbuf;
size_t wbufsize;
- void * rbuf;
+ void *rbuf;
u16 rbufsize;
u16 cid;
diff --git a/drivers/net/usb/qcusbnet/structs.h b/drivers/net/usb/qcusbnet/structs.h
index 0f9f4eb..2999e62 100755
--- a/drivers/net/usb/qcusbnet/structs.h
+++ b/drivers/net/usb/qcusbnet/structs.h
@@ -27,22 +27,15 @@
#include <linux/cdev.h>
#include <linux/kthread.h>
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,24)
- #include "usbnet.h"
-#else
- #include <linux/usb/usbnet.h>
-#endif
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)
- #include <linux/fdtable.h>
-#else
- #include <linux/file.h>
-#endif
-
-#define DBG(format, arg...) \
- if (debug == 1) { \
- printk(KERN_INFO "QCUSBNet2k::%s " format, __FUNCTION__, ## arg); \
- }
+#include <linux/usb/usbnet.h>
+
+#include <linux/fdtable.h>
+
+#define DBG(fmt, arg...) \
+do { \
+ if (debug == 1) \
+ printk(KERN_INFO "QCUSBNet2k::%s " fmt, __func__, ##arg); \
+} while (0)
struct qcusbnet;
--
1.7.3.1.g432b3.dirty
^ permalink raw reply related
* [PATCH 0/2] qcusbnet: Cleanups
From: Joe Perches @ 2010-09-29 2:39 UTC (permalink / raw)
To: Elly Jones; +Cc: netdev, dbrownell, mjg59, jglasgow, msb, olofj
In-Reply-To: <20100928171026.GB6083@google.com>
Perhaps some of these cleanups are in order?
Joe Perches (2):
drivers/net/usb/qcusbnet: Checkpatch cleanups
drivers/net/usb/qcusbnet/qmidevice: Remove printhex, use print_hex_dump
drivers/net/usb/qcusbnet/qcusbnet.c | 201 ++++++----------
drivers/net/usb/qcusbnet/qmi.c | 32 ++--
drivers/net/usb/qcusbnet/qmidevice.c | 451 ++++++++++++++++------------------
drivers/net/usb/qcusbnet/qmidevice.h | 1 -
drivers/net/usb/qcusbnet/structs.h | 25 +--
5 files changed, 307 insertions(+), 403 deletions(-)
--
1.7.3.1.g432b3.dirty
^ permalink raw reply
* [PATCH] sctp: implement SIOCINQ ioctl() (take 3)
From: Diego Elio Pettenò @ 2010-09-29 1:51 UTC (permalink / raw)
To: netdev, linux-sctp; +Cc: Diego Elio 'Flameeyes' Pettenò
From: Diego Elio 'Flameeyes' Pettenò <flameeyes@gmail.com>
This simple patch copies the current approach for SIOCINQ ioctl() from DCCP
into SCTP so that the userland code working with SCTP can use a similar
interface across different protocols to know how much space to allocate for
a buffer.
Signed-off-by: Diego Elio Pettenò <flameeyes@gmail.com>
---
net/sctp/socket.c | 35 ++++++++++++++++++++++++++++++++++-
1 files changed, 34 insertions(+), 1 deletions(-)
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index ca44917..199ec05 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -3595,7 +3595,40 @@ out:
/* The SCTP ioctl handler. */
SCTP_STATIC int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
{
- return -ENOIOCTLCMD;
+ int rc = -ENOTCONN;
+
+ sctp_lock_sock(sk);
+
+ /*
+ * SEQPACKET-style sockets in LISTENING state are valid, for
+ * SCTP, so only discard TCP-style sockets in LISTENING state.
+ */
+ if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING))
+ goto out;
+
+ switch (cmd) {
+ case SIOCINQ: {
+ struct sk_buff *skb;
+ unsigned int amount = 0;
+
+ skb = skb_peek(&sk->sk_receive_queue);
+ if (skb != NULL) {
+ /*
+ * We will only return the amount of this packet since
+ * that is all that will be read.
+ */
+ amount = skb->len;
+ }
+ rc = put_user(amount, (int __user *)arg);
+ }
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ break;
+ }
+out:
+ sctp_release_sock(sk);
+ return rc;
}
/* This is the function which gets called during socket creation to
--
1.7.3
^ permalink raw reply related
* VLAN GARP triggers a lot of timers if linux compiled with VLAN_8021Q_GVRP
From: didier @ 2010-09-29 0:35 UTC (permalink / raw)
To: netdev; +Cc: kaber
Hi,
In my understanding GARP is using a periodic timer but in net/802/garp.c:
garp_join_timer_arm()
delay = (u64)msecs_to_jiffies(garp_join_time)* net_random() >> 32;
mod_timer(&app->join_timer, jiffies +delay)
Isn't this stuff triggering a *lot* of events?
Didier
^ permalink raw reply
* Re: pull-request: bluetooth-2.6 2010-09-27
From: Gustavo F. Padovan @ 2010-09-28 22:49 UTC (permalink / raw)
To: David Miller
Cc: linville-2XuSBdqkA4R54TAoqtyWWQ, marcel-kz+m5ild9QBg9hUCZPvPmw,
linux-bluetooth-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20100927.200016.226762808.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
* David Miller <davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org> [2010-09-27 20:00:16 -0700]:
> From: "Gustavo F. Padovan" <padovan-Y3ZbgMPKUGA34EUeqzHoZw@public.gmane.org>
> Date: Mon, 27 Sep 2010 23:30:35 -0300
>
> > And a fix for a deadlock issue between the sk_sndbuf and the backlog
> > queue in ERTM. The rest are also needed bug fixes.
>
> This fix is still under discussion.
>
> That change effects quite a few code paths. And when I looked
> at them, I was not at all convinced that dropping the socket
> lock like that is safe.
>
> Are you sure there are no pieces of socket or socket related state
> that might change under us while we drop that lock, which would thus
> make the operation suddenly invalid or cause a state corruption or
> crash?
We can group all the code paths in only two different code paths. One
wirh SCO, L2CAP Basic Mode and L2CAP Streaming Mode once they are very
similar and other for ERTM, a more complicated protocol.
For the first group the only bottom half action we have are incoming data,
which doesn't affect the sk states, and disconnection request, that can
change the sk states. We guarantee that this won't affect by checking the
sk_err after get the lock again. Looking to the code again we might
also want to check the sk->sk_shutdown value like TCP does inside
sk_stream_wait_memory().
Actually sk_stream_wait_memory is another point why it's safe to release
the lock and block waiting for memory. We've been doing that safely in
protocols like TCP, SCTP and DCCP for a long time.
Back to patch, the other code path it affects is the ERTM one, besides
the incoming data we have other bottom halves actions, but in the end the
only action that can affect ERTM flow is closing the channeli, but we are
prepared for that by checking the sk->sk_err and sk->sk_shutdown when we
get the lock back.
---
Bluetooth: Fix deadlock in the ERTM logic
The Enhanced Retransmission Mode(ERTM) is a realiable mode of operation
of the Bluetooth L2CAP layer. Think on it like a simplified version of
TCP.
The problem we were facing here was a deadlock. ERTM uses a backlog
queue to queue incomimg packets while the user is helding the lock. At
some moment the sk_sndbuf can be exceeded and we can't alloc new skbs
then the code sleep with the lock to wait for memory, that stalls the
ERTM connection once we can't read the acknowledgements packets in the
backlog queue to free memory and make the allocation of outcoming skb
successful.
This patch actually affect all users of bt_skb_send_alloc(), i.e., all
L2CAP modes and SCO.
We are safe against socket states changes or channels deletion while the
we are sleeping wait memory. Checking for the sk->sk_err and
sk->sk_shutdown make the code safe, since any action that can leave the
socket or the channel in a not usable state set one of the struct
members at least. Then we can check both of them when getting the lock
again and return with the proper error if something unexpected happens.
Signed-off-by: Gustavo F. Padovan <padovan-Y3ZbgMPKUGA34EUeqzHoZw@public.gmane.org>
Signed-off-by: Ulisses Furquim <ulisses-Y3ZbgMPKUGA34EUeqzHoZw@public.gmane.org>
---
include/net/bluetooth/bluetooth.h | 18 ++++++++++++++++++
1 files changed, 18 insertions(+), 0 deletions(-)
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 27a902d..e8d64ba 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -161,12 +161,30 @@ static inline struct sk_buff *bt_skb_send_alloc(struct sock *sk, unsigned long l
{
struct sk_buff *skb;
+ release_sock(sk);
if ((skb = sock_alloc_send_skb(sk, len + BT_SKB_RESERVE, nb, err))) {
skb_reserve(skb, BT_SKB_RESERVE);
bt_cb(skb)->incoming = 0;
}
+ lock_sock(sk);
+
+ if (!skb && *err)
+ return NULL;
+
+ *err = sock_error(sk);
+ if (*err)
+ goto out;
+
+ if (sk->sk_shutdown) {
+ *err = ECONNRESET;
+ goto out;
+ }
return skb;
+
+out:
+ kfree_skb(skb);
+ return NULL;
}
int bt_err(__u16 code);
--
1.7.3
--
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi
^ permalink raw reply related
* Re: [RFC] Online firmware upgrade in non-embedded systems
From: Carl-Daniel Hailfinger @ 2010-09-28 22:41 UTC (permalink / raw)
To: Ben Hutchings; +Cc: netdev, linux-kernel, linux-mtd, sf-linux-drivers, flashrom
In-Reply-To: <1285696787.2282.45.camel@achroite.uk.solarflarecom.com>
[adding flashrom@flashrom.org to CC, senders will be whitelisted after a
short delay]
On 28.09.2010 19:59, Ben Hutchings wrote:
> Network and disk controllers normally have at least some firmware in
> flash to support their use as boot devices. [...]
>
> Currently the sfc network driver is optionally combined with an MTD
> driver (CONFIG_SFC_MTD) which exposes all upgradable firmware and
> configuration partitions in flash. This works nicely in kernels with
> MTD enabled, but since MTD is mainly used in embedded systems with
> on-board flash it is often disabled in distribution kernels and custom
> kernels alike. This leaves users of sfc unable to upgrade firmware
> without rebuilding the kernel or booting some other distribution. The
> lack of widespread MTD support is a regular cause of support requests.
>
> There are two main alternatives I'm aware of:
>
> - Use the ethtool ETHTOOL_SEEPROM [...]
>
> - Use the ethtool ETHTOOL_FLASHDEV command [..]
>
> Of course these are both specific to network devices; it seems deisrable
> to have a more general convention for online firmware upgrades. MTDs
> clearly are more generally applicable, and pretty much every computer
> does have flash storage for firmware and boot configuration, so perhaps
> it should be treated as more of a standard feature?
>
Given that the flashrom utility <http://www.flashrom.org/> (GPLv2)
supports flashing many network cards, SATA/PATA controllers, graphics
cards, and of course the main system firmware/BIOS/EFI, and it does that
from userspace without any kernel support, using flashrom for those
purposes makes sense IMHO.
flashrom works fine under Linux 2.4, 2.6 and pretty much every other OS
out there. There is a focus on x86, but MIPS, PowerPC and other
architectures are supported as well. flashrom is independent of the MTD
framework, so you don't need to boot a special kernel for a firmware
upgrade.
That said, writing a flashrom driver for your hardware is pretty easy.
You could even use existing kernel code for your device as a template
for the flashrom driver since the license is compatible.
If you have any questions, please ask.
Regards,
Carl-Daniel
^ permalink raw reply
* Re: [PATCH] net: Fix IPv6 PMTU disc. w/ asymmetric routes
From: Maciej Żenczykowski @ 2010-09-28 22:37 UTC (permalink / raw)
To: David Miller; +Cc: netdev, Hideaki Yoshifuji
In-Reply-To: <20100928.135800.39205209.davem@davemloft.net>
> Please handle all 4 cases just like the ipv4 routing code does:
>
> { saddr = SADDR, ifindex = dev->ifindex }
> { saddr = SADDR, ifindex = 0 }
> { saddr = INADDR_ANY, ifindex = dev->ifindex }
> { saddr = INADDR_ANY, ifindex = 0 }
>
> I believe I've specifically asked for this every time someone mentioned that they wanted to fix this issue.
I believe that is exactly what the following code fragment from the
above patch does:
+ rt6_do_pmtu_disc(daddr, saddr, net, pmtu, 0);
+ rt6_do_pmtu_disc(daddr, saddr, net, pmtu, dev->ifindex);
+ rt6_do_pmtu_disc(daddr, NULL, net, pmtu, 0);
+ rt6_do_pmtu_disc(daddr, NULL, net, pmtu, dev->ifindex);
The last two of these lines were added to the patch last time around
per your feedback - precisely to address this issue.
I reposted with those changes and you appeared to be ok with the
patch, and your only comment was to add a 'Signed-off-by' line.
Side note:
* I still think that handling the saddr == NULL ie. INADDR_ANY case is
entirely superfluous, since it doesn't actually iterate through all
possible source addresses. With IPv6 there can be many, many possible
source addresses (just think of link local vs global public vs privacy
addresses and then tack on 6to4 and mobility, etc... for example I see
13 ipv6 addresses on eth0 on my desktop at home, 12 of them globally
reachable).
* Currently PMTU discovery is broken. With this patch with all 4
lines we end up doing PMTU discovery per source address that isn't the
default source address. Without those last two lines we'd just do
PMTU discovery per source address. Not much of a difference, both
allow PMTU discovery to actually happen.
Except:
Imagine a machine with a 1500 MTU network card and 2 source addresses:
- one default v6 native 'A'
- one additional 6to4 address 'B'
The native address can reach the internet with a max MTU of 1500.
While due to source address based routing the 6to4 address goes
through a sit ipv4 based tunnel and thus has a max MTU of 1480 (1500 -
20 bytes IPv4 header).
Imagine a remote machine with v6 address 'Z' (our default address to
reach 'Z' is 'A').
Clearly the PMTU of A->Z is 1500, while the PMTU of B->Z is 1480.
[Assume there's nothing which would cause them to be lower.]
If you do PMTU discovery of A->Z, you will indeed get 1500, and mark
that as the PMTU of A->Z.
However if you do PMTU discovery of B->Z, you will indeed get 1480,
but you will mark that as the PMTU of both B->Z and A->Z (since A is
the default to reach Z).
Suddenly the PMTU of A->Z _needlessly and incorrectly_ dropped from
1500 to 1480 merely because of PMTU discovery being performed on B->Z.
And this is precisely why I think that adding those last two lines
that handle INADDR_ANY is actually more of a problem then solution
(indeed as far as I can tell, adding them doesn't actually fix any
problem - the code works just fine and performs PMTU discovery
correctly with just the first two lines).
Thankfully ending up with a lower MTU than actually possible is only a
slight performance problem, while not doing PMTU discovery correctly
at all (current state with asymmetric routing) is a big issue.
Hence regardless of whether this patch includes INADDR_ANY or not the
end result will still be an improvement.
> And the ipv4 side is a good guide in other ways, it uses a set of two arrays so you can just loop over them, making your rt6_do_pmtu_disc() calls along the way.
Sure, I can change it to use arrays, although I don't see any
particular improvement in performance (even if rt6_do_pmtu_disc was
inlined) or readability or anything.
^ permalink raw reply
* Re: [PATCH v2 1/2] Phonet: Implement Pipe Controller to support Nokia Slim Modems
From: Rémi Denis-Courmont @ 2010-09-28 22:25 UTC (permalink / raw)
To: ext Kumar A Sanghvi
Cc: davem@davemloft.net, netdev@vger.kernel.org,
STEricsson_nomadik_linux@list.st.com,
sudeep.divakaran@stericsson.com, gulshan.karmani@stericsson.com,
Linus Walleij
In-Reply-To: <1285564079-23066-1-git-send-email-kumar.sanghvi@stericsson.com>
On Monday 27 September 2010 08:07:59 ext Kumar A Sanghvi, you wrote:
> From: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
>
> Phonet stack assumes the presence of Pipe Controller, either in Modem or
> on Application Processing Engine user-space for the Pipe data.
> Nokia Slim Modems like WG2.5 used in ST-Ericsson U8500 platform do not
> implement Pipe controller in them.
> This patch adds Pipe Controller implemenation to Phonet stack to support
> Pipe data over Phonet stack for Nokia Slim Modems.
>
> Signed-off-by: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
> Acked-by: Linus Walleij <linus.walleij@stericsson.com>
> ---
> Changes:
>
> -v2: Correction for header retrieving after pskb_may_pull
>
> include/linux/phonet.h | 5 +
> include/net/phonet/pep.h | 21 +++
> net/phonet/Kconfig | 11 ++
> net/phonet/pep.c | 448
> +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 479
> insertions(+), 6 deletions(-)
>
> diff --git a/include/linux/phonet.h b/include/linux/phonet.h
> index 85e14a8..96f5625 100644
> --- a/include/linux/phonet.h
> +++ b/include/linux/phonet.h
> @@ -36,6 +36,11 @@
> /* Socket options for SOL_PNPIPE level */
> #define PNPIPE_ENCAP 1
> #define PNPIPE_IFINDEX 2
> +#define PNPIPE_CREATE 3
> +#define PNPIPE_ENABLE 4
> +#define PNPIPE_DISABLE 5
> +#define PNPIPE_DESTROY 6
> +#define PNPIPE_INQ 7
As far as I know, you don't need to do that in kernel space. I don't know the
internals of the STE modem. Regardless of having or not having a pipe
controller, Linux userspace can send pipe messages using a plain Phonet
datagram socket. This avoids adding ioctl()'s and protocol stuff in kernel
space. Then, as far as kernel is concerned, only small changes to the data
path would be required.
Both the Nokia modem plugin for oFono, and the (closed-source) Nokia N900 CSD-
GPRS service work that way. In other words, the pipe 'signaling' is done in
userspace, while the pipe 'data' is done in kernel space for optimal
performance.
For some background - Phonet pipes work very much like FTP. There are two
endpoints exchaning data, and one client ('owner') deciding which endpoints
and when to establish a pipe between. In most case the client is also one of
the endpoint, but this is not required. With this patch, the client is tied to
being one of the endpoint. Arguably, this is not a problem for most usecases.
But I am not sure this belongs in *kernel* space. Sticking to the same
comparison: with FTP, you have TCP (data path) in kernel, but not FTP itself
(signaling).
> @@ -791,6 +1171,48 @@ static int pep_setsockopt(struct sock *sk, int level,
> int optname,
>
> lock_sock(sk);
> switch (optname) {
> +#ifdef CONFIG_PHONET_PIPECTRLR
> + case PNPIPE_CREATE:
> + if (val) {
> + if (pn->pipe_state > PIPE_IDLE) {
> + err = -EFAULT;
Why EFAULT here? I can't see any user-space memory access failure.
> + break;
> + }
> + remote_pep = val & 0xFFFF;
> + pipe_handle = (val >> 16) & 0xFF;
> + pn->remote_pep = remote_pep;
> + err = pipe_handler_create_pipe(sk, pipe_handle,
> + PNPIPE_CREATE);
> + break;
> + }
> +
> + case PNPIPE_ENABLE:
> + if (pn->pipe_state != PIPE_DISABLED) {
> + err = -EFAULT;
Same here.
> + break;
> + }
> + err = pipe_handler_enable_pipe(sk, PNPIPE_ENABLE);
> + break;
> +
> + case PNPIPE_DISABLE:
> + if (pn->pipe_state != PIPE_ENABLED) {
> + err = -EFAULT;
Ditto.
> + break;
> + }
> +
> + err = pipe_handler_enable_pipe(sk, PNPIPE_DISABLE);
> + break;
> +
> + case PNPIPE_DESTROY:
> + if (pn->pipe_state < PIPE_DISABLED) {
> + err = -EFAULT;
And here,
> @@ -877,11 +1313,11 @@ static int pipe_skb_send(struct sock *sk, struct
> sk_buff *skb) } else
> ph->message_id = PNS_PIPE_DATA;
> ph->pipe_handle = pn->pipe_handle;
> -
> - err = pn_skb_send(sk, skb, &pipe_srv);
> - if (err && pn_flow_safe(pn->tx_fc))
> - atomic_inc(&pn->tx_credits);
> - return err;
> +#ifdef CONFIG_PHONET_PIPECTRLR
> + return pn_skb_send(sk, skb, &spn);
> +#else
> + return pn_skb_send(sk, skb, &pipe_srv);
> +#endif
> }
This reintroduces the bug that I fixed in
1a98214feef2221cd7c24b17cd688a5a9d85b2ea :-(
--
Rémi Denis-Courmont
Nokia Devices R&D, Maemo Software, Helsinki
^ permalink raw reply
* (unknown),
From: FinancialAid @ 2010-09-28 22:09 UTC (permalink / raw)
An emergency,please respond.
I am Mrs. Ksenia Gutseriyev, the wife of Russian multi billionaire Mr. Mikhail Gutseriyev's, the former owner of Russneft Oil Company in Russia, i have a proposal for you, if intrested contact via my box: ksenia.gutseri@gala.net
^ permalink raw reply
* Re: [PATCHv2 net-next-2.6 0/5] XFRM,IPv6: Removal of RH2/HAO from IPsec-protected MIPv6 traffic
From: David Miller @ 2010-09-28 21:40 UTC (permalink / raw)
To: arno; +Cc: eric.dumazet, herbert, yoshfuji, netdev
In-Reply-To: <87r5gdtuxv.fsf@small.ssi.corp>
From: arno@natisbad.org (Arnaud Ebalard)
Date: Tue, 28 Sep 2010 23:33:16 +0200
> Before following the (dumb) #ifdef path, I was about to do that but
> worried about the penalty of the additional xfrm_state_get/put_afinfo()
> calls on each packet I was about to add. Should I just reduce my amount
> of coffee or is it a valid concern?
Indeed, it is.
Even without the concern of afinfo refcounting, this test is very
heavy handed for the packet path.
Can you make it small enough that it can reasonably be inlined?
^ permalink raw reply
* Re: [PATCHv2 net-next-2.6 0/5] XFRM,IPv6: Removal of RH2/HAO from IPsec-protected MIPv6 traffic
From: Arnaud Ebalard @ 2010-09-28 21:33 UTC (permalink / raw)
To: David Miller; +Cc: eric.dumazet, herbert, yoshfuji, netdev
In-Reply-To: <20100928.133849.112575548.davem@davemloft.net>
Hi,
David Miller <davem@davemloft.net> writes:
> Try again, this time with ipv6 modular:
>
> net/built-in.o: In function `xfrm_input_addr_check':
> /home/davem/src/GIT/net-next-2.6/net/xfrm/xfrm_input.c:115: undefined reference to `xfrm6_input_addr_check'
>
> You can't put xfrm6_input_addr_check into the ipv6.o object build if you want to
> call it from the generic xfrm stack which is always built statically.
>
> Put this and xfrm4_input_addr_check where it belongs, as an afinfo->op()
Before following the (dumb) #ifdef path, I was about to do that but
worried about the penalty of the additional xfrm_state_get/put_afinfo()
calls on each packet I was about to add. Should I just reduce my amount
of coffee or is it a valid concern?
Cheers,
a+
^ permalink raw reply
* Re: [PATCH] Phonet: Correct header retrieval after pskb_may_pull
From: Rémi Denis-Courmont @ 2010-09-28 21:23 UTC (permalink / raw)
To: ext Kumar A Sanghvi
Cc: netdev@vger.kernel.org, davem@davemloft.net,
eric.dumazet@gmail.com, gulshan.karmani@stericsson.com,
Linus Walleij
In-Reply-To: <1285665042-20548-1-git-send-email-kumar.sanghvi@stericsson.com>
On Tuesday 28 September 2010 12:10:42 ext Kumar A Sanghvi, you wrote:
> From: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
>
> Retrieve the header after doing pskb_may_pull since, pskb_may_pull
> could change the buffer structure.
>
> This is based on the comment given by Eric Dumazet on Phonet
> Pipe controller patch for a similar problem.
>
> Signed-off-by: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
> Acked-by: Linus Walleij <linus.walleij@stericsson.com>
> ---
> net/phonet/pep.c | 3 ++-
> 1 files changed, 2 insertions(+), 1 deletions(-)
>
> diff --git a/net/phonet/pep.c b/net/phonet/pep.c
> index 7bf23cf..9746c6d 100644
> --- a/net/phonet/pep.c
> +++ b/net/phonet/pep.c
> @@ -507,12 +507,13 @@ static void pipe_grant_credits(struct sock *sk)
> static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
> {
> struct pep_sock *pn = pep_sk(sk);
> - struct pnpipehdr *hdr = pnp_hdr(skb);
> + struct pnpipehdr *hdr;
> int wake = 0;
>
> if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
> return -EINVAL;
>
> + hdr = pnp_hdr(skb);
> if (hdr->data[0] != PN_PEP_TYPE_COMMON) {
> LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP type: %u\n",
> (unsigned)hdr->data[0]);
Acked-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
Thanks!
--
Rémi Denis-Courmont
Nokia Devices R&D, Maemo Software, Helsinki
^ permalink raw reply
* Re: [PATCH] Add Qualcomm Gobi 2000 driver.
From: Dan Williams @ 2010-09-28 21:23 UTC (permalink / raw)
To: Elly Jones; +Cc: netdev, dbrownell, mjg59, jglasgow, msb, olofj
In-Reply-To: <20100928171026.GB6083@google.com>
On Tue, 2010-09-28 at 13:10 -0400, Elly Jones wrote:
> From: Elizabeth Jones <ellyjones@google.com>
>
> This driver is a rewrite of the original Qualcomm GPL driver, released as part
> of Qualcomm's "Code Aurora" initiative. The driver has been transformed into
> Linux kernel style and made to use kernel APIs where appropriate; some bugs have
> also been fixed. Note that the device in question requires firmware and a
> firmware loader; the latter has been written by mjg (see
> http://www.codon.org.uk/~mjg59/gobi_loader/).
Also, is there any way you can wrangle Gobi 1000 support out of
Qualcomm? I tried the driver with a 1000 part when Qualcomm originally
posted the 2K net driver back in June and it resulted in a panic when
the driver didn't get the response it expected. I suppose I can try
again with this code too, but getting some feedback from Qualcomm that
1K parts might work here would be nice too.
Also, are 3K devices supported by this code? The 3K net and serial
driver was recently posted on codeaurora.
Dan
> Signed-Off-By: Elizabeth Jones <ellyjones@google.com>
> Signed-Off-By: Jason Glasgow <jglasgow@google.com>
> ---
> drivers/net/usb/Kconfig | 6 +
> drivers/net/usb/Makefile | 2 +-
> drivers/net/usb/qcusbnet/Makefile | 2 +
> drivers/net/usb/qcusbnet/qcusbnet.c | 706 +++++++++++++++
> drivers/net/usb/qcusbnet/qcusbnet.h | 24 +
> drivers/net/usb/qcusbnet/qmi.c | 358 ++++++++
> drivers/net/usb/qcusbnet/qmi.h | 67 ++
> drivers/net/usb/qcusbnet/qmidevice.c | 1621 ++++++++++++++++++++++++++++++++++
> drivers/net/usb/qcusbnet/qmidevice.h | 36 +
> drivers/net/usb/qcusbnet/readme.txt | 118 +++
> drivers/net/usb/qcusbnet/structs.h | 99 ++
> 11 files changed, 3038 insertions(+), 1 deletions(-)
> create mode 100755 drivers/net/usb/qcusbnet/Makefile
> create mode 100644 drivers/net/usb/qcusbnet/qcusbnet.c
> create mode 100644 drivers/net/usb/qcusbnet/qcusbnet.h
> create mode 100755 drivers/net/usb/qcusbnet/qmi.c
> create mode 100755 drivers/net/usb/qcusbnet/qmi.h
> create mode 100755 drivers/net/usb/qcusbnet/qmidevice.c
> create mode 100755 drivers/net/usb/qcusbnet/qmidevice.h
> create mode 100755 drivers/net/usb/qcusbnet/readme.txt
> create mode 100755 drivers/net/usb/qcusbnet/structs.h
>
> diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
> index d7b7018..8a210d9 100644
> --- a/drivers/net/usb/Kconfig
> +++ b/drivers/net/usb/Kconfig
> @@ -406,4 +406,10 @@ config USB_SIERRA_NET
> To compile this driver as a module, choose M here: the
> module will be called sierra_net.
>
> +config USB_NET_GOBI
> + tristate "Qualcomm Gobi"
> + depends on USB_USBNET
> + help
> + Choose this option if you have a Qualcomm Gobi 2000 cellular modem.
> +
> endmenu
> diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
> index b13a279..f3f702f 100644
> --- a/drivers/net/usb/Makefile
> +++ b/drivers/net/usb/Makefile
> @@ -25,4 +25,4 @@ obj-$(CONFIG_USB_NET_INT51X1) += int51x1.o
> obj-$(CONFIG_USB_CDC_PHONET) += cdc-phonet.o
> obj-$(CONFIG_USB_IPHETH) += ipheth.o
> obj-$(CONFIG_USB_SIERRA_NET) += sierra_net.o
> -
> +obj-$(CONFIG_USB_NET_GOBI) += qcusbnet/
> diff --git a/drivers/net/usb/qcusbnet/Makefile b/drivers/net/usb/qcusbnet/Makefile
> new file mode 100755
> index 0000000..a186c19
> --- /dev/null
> +++ b/drivers/net/usb/qcusbnet/Makefile
> @@ -0,0 +1,2 @@
> +obj-$(CONFIG_USB_NET_GOBI) += qcusbnet2k.o
> +qcusbnet2k-objs += qcusbnet.o qmidevice.o qmi.o
> diff --git a/drivers/net/usb/qcusbnet/qcusbnet.c b/drivers/net/usb/qcusbnet/qcusbnet.c
> new file mode 100644
> index 0000000..a075b55
> --- /dev/null
> +++ b/drivers/net/usb/qcusbnet/qcusbnet.c
> @@ -0,0 +1,706 @@
> +/* qcusbnet.c - gobi network device
> + * Copyright (c) 2010, Code Aurora Forum. 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
> + * only version 2 as published by the Free Software Foundation.
> +
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> +
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> + * 02110-1301, USA.
> + */
> +
> +#include "structs.h"
> +#include "qmidevice.h"
> +#include "qmi.h"
> +#include "qcusbnet.h"
> +
> +#define DRIVER_VERSION "1.0.110"
> +#define DRIVER_AUTHOR "Qualcomm Innovation Center"
> +#define DRIVER_DESC "QCUSBNet2k"
> +
> +int debug;
> +static struct class *devclass;
> +
> +int qc_suspend(struct usb_interface *iface, pm_message_t event)
> +{
> + struct usbnet * usbnet;
> + struct qcusbnet * dev;
> +
> + if (!iface)
> + return -ENOMEM;
> +
> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
> + usbnet = usb_get_intfdata(iface);
> +#else
> + usbnet = iface->dev.platform_data;
> +#endif
> +
> + if (!usbnet || !usbnet->net) {
> + DBG("failed to get netdevice\n");
> + return -ENXIO;
> + }
> +
> + dev = (struct qcusbnet *)usbnet->data[0];
> + if (!dev) {
> + DBG("failed to get QMIDevice\n");
> + return -ENXIO;
> + }
> +
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
> + if (!usbnet->udev->auto_pm)
> +#else
> + if (!(event.event & PM_EVENT_AUTO))
> +#endif
> + {
> + DBG("device suspended to power level %d\n",
> + event.event);
> + qc_setdown(dev, DOWN_DRIVER_SUSPENDED);
> + } else {
> + DBG("device autosuspend\n");
> + }
> +
> + if (event.event & PM_EVENT_SUSPEND) {
> + qc_stopread(dev);
> + usbnet->udev->reset_resume = 0;
> + iface->dev.power.power_state.event = event.event;
> + } else {
> + usbnet->udev->reset_resume = 1;
> + }
> +
> + return usbnet_suspend(iface, event);
> +}
> +
> +static int qc_resume(struct usb_interface * iface)
> +{
> + struct usbnet *usbnet;
> + struct qcusbnet *dev;
> + int ret;
> + int oldstate;
> +
> + if (iface == 0)
> + return -ENOMEM;
> +
> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
> + usbnet = usb_get_intfdata(iface);
> +#else
> + usbnet = iface->dev.platform_data;
> +#endif
> +
> + if (!usbnet || !usbnet->net) {
> + DBG("failed to get netdevice\n");
> + return -ENXIO;
> + }
> +
> + dev = (struct qcusbnet *)usbnet->data[0];
> + if (!dev) {
> + DBG("failed to get QMIDevice\n");
> + return -ENXIO;
> + }
> +
> + oldstate = iface->dev.power.power_state.event;
> + iface->dev.power.power_state.event = PM_EVENT_ON;
> + DBG("resuming from power mode %d\n", oldstate);
> +
> + if (oldstate & PM_EVENT_SUSPEND) {
> + qc_cleardown(dev, DOWN_DRIVER_SUSPENDED);
> +
> + ret = usbnet_resume(iface);
> + if (ret) {
> + DBG("usbnet_resume error %d\n", ret);
> + return ret;
> + }
> +
> + ret = qc_startread(dev);
> + if (ret) {
> + DBG("qc_startread error %d\n", ret);
> + return ret;
> + }
> +
> + complete(&dev->worker.work);
> + } else {
> + DBG("nothing to resume\n");
> + return 0;
> + }
> +
> + return ret;
> +}
> +
> +static int qcnet_bind(struct usbnet *usbnet, struct usb_interface *iface)
> +{
> + int numends;
> + int i;
> + struct usb_host_endpoint *endpoint = NULL;
> + struct usb_host_endpoint *in = NULL;
> + struct usb_host_endpoint *out = NULL;
> +
> + if (iface->num_altsetting != 1) {
> + DBG("invalid num_altsetting %u\n", iface->num_altsetting);
> + return -EINVAL;
> + }
> +
> + if (iface->cur_altsetting->desc.bInterfaceNumber != 0) {
> + DBG("invalid interface %d\n",
> + iface->cur_altsetting->desc.bInterfaceNumber);
> + return -EINVAL;
> + }
> +
> + numends = iface->cur_altsetting->desc.bNumEndpoints;
> + for (i = 0; i < numends; i++) {
> + endpoint = iface->cur_altsetting->endpoint + i;
> + if (!endpoint) {
> + DBG("invalid endpoint %u\n", i);
> + return -EINVAL;
> + }
> +
> + if (usb_endpoint_dir_in(&endpoint->desc)
> + && !usb_endpoint_xfer_int(&endpoint->desc)) {
> + in = endpoint;
> + } else if (!usb_endpoint_dir_out(&endpoint->desc)) {
> + out = endpoint;
> + }
> + }
> +
> + if (!in || !out) {
> + DBG("invalid endpoints\n");
> + return -EINVAL;
> + }
> +
> + if (usb_set_interface(usbnet->udev,
> + iface->cur_altsetting->desc.bInterfaceNumber, 0)) {
> + DBG("unable to set interface\n");
> + return -EINVAL;
> + }
> +
> + usbnet->in = usb_rcvbulkpipe(usbnet->udev, in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
> + usbnet->out = usb_sndbulkpipe(usbnet->udev, out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
> +
> + DBG("in %x, out %x\n",
> + in->desc.bEndpointAddress,
> + out->desc.bEndpointAddress);
> +
> +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
> + iface->dev.platform_data = usbnet;
> +#endif
> + return 0;
> +}
> +
> +static void qcnet_unbind(struct usbnet *usbnet, struct usb_interface *iface)
> +{
> + struct qcusbnet *dev = (struct qcusbnet *)usbnet->data[0];
> +
> + netif_carrier_off(usbnet->net);
> + qc_deregister(dev);
> +
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
> + kfree(usbnet->net->netdev_ops);
> + usbnet->net->netdev_ops = NULL;
> +#endif
> +
> +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
> + iface->dev.platform_data = NULL;
> +#endif
> +
> + kfree(dev);
> +}
> +
> +static void qcnet_urbhook(struct urb * urb)
> +{
> + unsigned long flags;
> + struct worker * worker = urb->context;
> + if (!worker) {
> + DBG("bad context\n");
> + return;
> + }
> +
> + if (urb->status) {
> + DBG("urb finished with error %d\n", urb->status);
> + }
> +
> + spin_lock_irqsave(&worker->active_lock, flags);
> + worker->active = ERR_PTR(-EAGAIN);
> + spin_unlock_irqrestore(&worker->active_lock, flags);
> + /* XXX-fix race against qcnet_stop()? */
> + complete(&worker->work);
> + usb_free_urb(urb);
> +}
> +
> +static void qcnet_txtimeout(struct net_device *netdev)
> +{
> + struct list_head *node, *tmp;
> + struct qcusbnet *dev;
> + struct worker *worker;
> + struct urbreq *req;
> + unsigned long activeflags, listflags;
> + struct usbnet *usbnet = netdev_priv(netdev);
> +
> + if (!usbnet || !usbnet->net) {
> + DBG("failed to get usbnet device\n");
> + return;
> + }
> +
> + dev = (struct qcusbnet *)usbnet->data[0];
> + if (!dev) {
> + DBG("failed to get QMIDevice\n");
> + return;
> + }
> + worker = &dev->worker;
> +
> + DBG("\n");
> +
> + spin_lock_irqsave(&worker->active_lock, activeflags);
> + if (worker->active)
> + usb_kill_urb(worker->active);
> + spin_unlock_irqrestore(&worker->active_lock, activeflags);
> +
> + spin_lock_irqsave(&worker->urbs_lock, listflags);
> + list_for_each_safe(node, tmp, &worker->urbs) {
> + req = list_entry(node, struct urbreq, node);
> + usb_free_urb(req->urb);
> + list_del(&req->node);
> + kfree(req);
> + }
> + spin_unlock_irqrestore(&worker->urbs_lock, listflags);
> +
> + complete(&worker->work);
> +}
> +
> +static int qcnet_worker(void *arg)
> +{
> + struct list_head *node, *tmp;
> + unsigned long activeflags, listflags;
> + struct urbreq *req;
> + int status;
> + struct usb_device *usbdev;
> + struct worker *worker = arg;
> + if (!worker) {
> + DBG("passed null pointer\n");
> + return -EINVAL;
> + }
> +
> + usbdev = interface_to_usbdev(worker->iface);
> +
> + DBG("traffic thread started\n");
> +
> + while (!kthread_should_stop()) {
> + wait_for_completion_interruptible(&worker->work);
> +
> + if (kthread_should_stop()) {
> + spin_lock_irqsave(&worker->active_lock, activeflags);
> + if (worker->active) {
> + usb_kill_urb(worker->active);
> + }
> + spin_unlock_irqrestore(&worker->active_lock, activeflags);
> +
> + spin_lock_irqsave(&worker->urbs_lock, listflags);
> + list_for_each_safe(node, tmp, &worker->urbs) {
> + req = list_entry(node, struct urbreq, node);
> + usb_free_urb(req->urb);
> + list_del(&req->node);
> + kfree(req);
> + }
> + spin_unlock_irqrestore(&worker->urbs_lock, listflags);
> +
> + break;
> + }
> +
> + spin_lock_irqsave(&worker->active_lock, activeflags);
> + if (IS_ERR(worker->active) && PTR_ERR(worker->active) == -EAGAIN) {
> + worker->active = NULL;
> + spin_unlock_irqrestore(&worker->active_lock, activeflags);
> + usb_autopm_put_interface(worker->iface);
> + spin_lock_irqsave(&worker->active_lock, activeflags);
> + }
> +
> + if (worker->active) {
> + spin_unlock_irqrestore(&worker->active_lock, activeflags);
> + continue;
> + }
> +
> + spin_lock_irqsave(&worker->urbs_lock, listflags);
> + if (list_empty(&worker->urbs)) {
> + spin_unlock_irqrestore(&worker->urbs_lock, listflags);
> + spin_unlock_irqrestore(&worker->active_lock, activeflags);
> + continue;
> + }
> +
> + req = list_first_entry(&worker->urbs, struct urbreq, node);
> + list_del(&req->node);
> + spin_unlock_irqrestore(&worker->urbs_lock, listflags);
> +
> + worker->active = req->urb;
> + spin_unlock_irqrestore(&worker->active_lock, activeflags);
> +
> + status = usb_autopm_get_interface(worker->iface);
> + if (status < 0) {
> + DBG("unable to autoresume interface: %d\n", status);
> + if (status == -EPERM)
> + {
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
> + usbdev->auto_pm = 0;
> +#endif
> + qc_suspend(worker->iface, PMSG_SUSPEND);
> + }
> +
> + spin_lock_irqsave(&worker->urbs_lock, listflags);
> + list_add(&req->node, &worker->urbs);
> + spin_unlock_irqrestore(&worker->urbs_lock, listflags);
> +
> + spin_lock_irqsave(&worker->active_lock, activeflags);
> + worker->active = NULL;
> + spin_unlock_irqrestore(&worker->active_lock, activeflags);
> +
> + continue;
> + }
> +
> + status = usb_submit_urb(worker->active, GFP_KERNEL);
> + if (status < 0) {
> + DBG("Failed to submit URB: %d. Packet dropped\n", status);
> + spin_lock_irqsave(&worker->active_lock, activeflags);
> + usb_free_urb(worker->active);
> + worker->active = NULL;
> + spin_unlock_irqrestore(&worker->active_lock, activeflags);
> + usb_autopm_put_interface(worker->iface);
> + complete(&worker->work);
> + }
> +
> + kfree(req);
> + }
> +
> + DBG("traffic thread exiting\n");
> + worker->thread = NULL;
> + return 0;
> +}
> +
> +static int qcnet_startxmit(struct sk_buff *skb, struct net_device *netdev)
> +{
> + unsigned long listflags;
> + struct qcusbnet *dev;
> + struct worker *worker;
> + struct urbreq *req;
> + void *data;
> + struct usbnet *usbnet = netdev_priv(netdev);
> +
> + DBG("\n");
> +
> + if (!usbnet || !usbnet->net) {
> + DBG("failed to get usbnet device\n");
> + return NETDEV_TX_BUSY;
> + }
> +
> + dev = (struct qcusbnet *)usbnet->data[0];
> + if (!dev) {
> + DBG("failed to get QMIDevice\n");
> + return NETDEV_TX_BUSY;
> + }
> + worker = &dev->worker;
> +
> + if (qc_isdown(dev, DOWN_DRIVER_SUSPENDED)) {
> + DBG("device is suspended\n");
> + dump_stack();
> + return NETDEV_TX_BUSY;
> + }
> +
> + req = kmalloc(sizeof(*req), GFP_ATOMIC);
> + if (!req) {
> + DBG("unable to allocate URBList memory\n");
> + return NETDEV_TX_BUSY;
> + }
> +
> + req->urb = usb_alloc_urb(0, GFP_ATOMIC);
> +
> + if (!req->urb) {
> + kfree(req);
> + DBG("unable to allocate URB\n");
> + return NETDEV_TX_BUSY;
> + }
> +
> + data = kmalloc(skb->len, GFP_ATOMIC);
> + if (!data) {
> + usb_free_urb(req->urb);
> + kfree(req);
> + DBG("unable to allocate URB data\n");
> + return NETDEV_TX_BUSY;
> + }
> + memcpy(data, skb->data, skb->len);
> +
> + usb_fill_bulk_urb(req->urb, dev->usbnet->udev, dev->usbnet->out,
> + data, skb->len, qcnet_urbhook, worker);
> +
> + spin_lock_irqsave(&worker->urbs_lock, listflags);
> + list_add_tail(&req->node, &worker->urbs);
> + spin_unlock_irqrestore(&worker->urbs_lock, listflags);
> +
> + complete(&worker->work);
> +
> + netdev->trans_start = jiffies;
> + dev_kfree_skb_any(skb);
> +
> + return NETDEV_TX_OK;
> +}
> +
> +static int qcnet_open(struct net_device *netdev)
> +{
> + int status = 0;
> + struct qcusbnet *dev;
> + struct usbnet *usbnet = netdev_priv(netdev);
> +
> + if (!usbnet) {
> + DBG("failed to get usbnet device\n");
> + return -ENXIO;
> + }
> +
> + dev = (struct qcusbnet *)usbnet->data[0];
> + if (!dev) {
> + DBG("failed to get QMIDevice\n");
> + return -ENXIO;
> + }
> +
> + DBG("\n");
> +
> + dev->worker.iface = dev->iface;
> + INIT_LIST_HEAD(&dev->worker.urbs);
> + dev->worker.active = NULL;
> + spin_lock_init(&dev->worker.urbs_lock);
> + spin_lock_init(&dev->worker.active_lock);
> + init_completion(&dev->worker.work);
> +
> + dev->worker.thread = kthread_run(qcnet_worker, &dev->worker, "qcnet_worker");
> + if (IS_ERR(dev->worker.thread))
> + {
> + DBG("AutoPM thread creation error\n");
> + return PTR_ERR(dev->worker.thread);
> + }
> +
> + qc_cleardown(dev, DOWN_NET_IFACE_STOPPED);
> + if (dev->open)
> + {
> + status = dev->open(netdev);
> + if (status == 0)
> + {
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
> + usb_autopm_enable(dev->iface);
> +#else
> + usb_autopm_put_interface(dev->iface);
> +#endif
> + }
> + } else {
> + DBG("no USBNetOpen defined\n");
> + }
> +
> + return status;
> +}
> +
> +int qcnet_stop(struct net_device *netdev)
> +{
> + struct qcusbnet *dev;
> + struct usbnet *usbnet = netdev_priv(netdev);
> +
> + if (!usbnet || !usbnet->net) {
> + DBG("failed to get netdevice\n");
> + return -ENXIO;
> + }
> +
> + dev = (struct qcusbnet *)usbnet->data[0];
> + if (!dev) {
> + DBG("failed to get QMIDevice\n");
> + return -ENXIO;
> + }
> +
> + qc_setdown(dev, DOWN_NET_IFACE_STOPPED);
> + complete(&dev->worker.work);
> + kthread_stop(dev->worker.thread);
> + DBG("thread stopped\n");
> +
> + if (dev->stop != NULL)
> + return dev->stop(netdev);
> + return 0;
> +}
> +
> +static const struct driver_info qc_netinfo =
> +{
> + .description = "QCUSBNet Ethernet Device",
> + .flags = FLAG_ETHER,
> + .bind = qcnet_bind,
> + .unbind = qcnet_unbind,
> + .data = 0,
> +};
> +
> +#define MKVIDPID(v,p) \
> + { \
> + USB_DEVICE(v,p), \
> + .driver_info = (unsigned long)&qc_netinfo, \
> + }
> +
> +static const struct usb_device_id qc_vidpids [] =
> +{
> + MKVIDPID(0x05c6, 0x9215), /* Acer Gobi 2000 */
> + MKVIDPID(0x05c6, 0x9265), /* Asus Gobi 2000 */
> + MKVIDPID(0x16d8, 0x8002), /* CMOTech Gobi 2000 */
> + MKVIDPID(0x413c, 0x8186), /* Dell Gobi 2000 */
> + MKVIDPID(0x1410, 0xa010), /* Entourage Gobi 2000 */
> + MKVIDPID(0x1410, 0xa011), /* Entourage Gobi 2000 */
> + MKVIDPID(0x1410, 0xa012), /* Entourage Gobi 2000 */
> + MKVIDPID(0x1410, 0xa013), /* Entourage Gobi 2000 */
> + MKVIDPID(0x03f0, 0x251d), /* HP Gobi 2000 */
> + MKVIDPID(0x05c6, 0x9205), /* Lenovo Gobi 2000 */
> + MKVIDPID(0x05c6, 0x920b), /* Generic Gobi 2000 */
> + MKVIDPID(0x04da, 0x250f), /* Panasonic Gobi 2000 */
> + MKVIDPID(0x05c6, 0x9245), /* Samsung Gobi 2000 */
> + MKVIDPID(0x1199, 0x9001), /* Sierra Wireless Gobi 2000 */
> + MKVIDPID(0x1199, 0x9002), /* Sierra Wireless Gobi 2000 */
> + MKVIDPID(0x1199, 0x9003), /* Sierra Wireless Gobi 2000 */
> + MKVIDPID(0x1199, 0x9004), /* Sierra Wireless Gobi 2000 */
> + MKVIDPID(0x1199, 0x9005), /* Sierra Wireless Gobi 2000 */
> + MKVIDPID(0x1199, 0x9006), /* Sierra Wireless Gobi 2000 */
> + MKVIDPID(0x1199, 0x9007), /* Sierra Wireless Gobi 2000 */
> + MKVIDPID(0x1199, 0x9008), /* Sierra Wireless Gobi 2000 */
> + MKVIDPID(0x1199, 0x9009), /* Sierra Wireless Gobi 2000 */
> + MKVIDPID(0x1199, 0x900a), /* Sierra Wireless Gobi 2000 */
> + MKVIDPID(0x05c6, 0x9225), /* Sony Gobi 2000 */
> + MKVIDPID(0x05c6, 0x9235), /* Top Global Gobi 2000 */
> + MKVIDPID(0x05c6, 0x9275), /* iRex Technologies Gobi 2000 */
> + { }
> +};
> +
> +MODULE_DEVICE_TABLE(usb, qc_vidpids);
> +
> +int qcnet_probe(struct usb_interface *iface, const struct usb_device_id *vidpids)
> +{
> + int status;
> + struct usbnet *usbnet;
> + struct qcusbnet *dev;
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
> + struct net_device_ops *netdevops;
> +#endif
> +
> + status = usbnet_probe(iface, vidpids);
> + if (status < 0) {
> + DBG("usbnet_probe failed %d\n", status);
> + return status;
> + }
> +
> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
> + usbnet = usb_get_intfdata(iface);
> +#else
> + usbnet = iface->dev.platform_data;
> +#endif
> +
> + if (!usbnet || !usbnet->net) {
> + DBG("failed to get netdevice\n");
> + return -ENXIO;
> + }
> +
> + dev = kmalloc(sizeof(struct qcusbnet), GFP_KERNEL);
> + if (!dev) {
> + DBG("falied to allocate device buffers");
> + return -ENOMEM;
> + }
> +
> + usbnet->data[0] = (unsigned long)dev;
> +
> + dev->usbnet = usbnet;
> +
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
> + dev->open = usbnet->net->open;
> + usbnet->net->open = qcnet_open;
> + dev->stop = usbnet->net->stop;
> + usbnet->net->stop = qcnet_stop;
> + usbnet->net->hard_start_xmit = qcnet_startxmit;
> + usbnet->net->tx_timeout = qcnet_txtimeout;
> +#else
> + netdevops = kmalloc(sizeof(struct net_device_ops), GFP_KERNEL);
> + if (!netdevops) {
> + DBG("falied to allocate net device ops");
> + return -ENOMEM;
> + }
> + memcpy(netdevops, usbnet->net->netdev_ops, sizeof(struct net_device_ops));
> +
> + dev->open = netdevops->ndo_open;
> + netdevops->ndo_open = qcnet_open;
> + dev->stop = netdevops->ndo_stop;
> + netdevops->ndo_stop = qcnet_stop;
> + netdevops->ndo_start_xmit = qcnet_startxmit;
> + netdevops->ndo_tx_timeout = qcnet_txtimeout;
> +
> + usbnet->net->netdev_ops = netdevops;
> +#endif
> +
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)
> + memset(&(dev->usbnet->stats), 0, sizeof(struct net_device_stats));
> +#else
> + memset(&(dev->usbnet->net->stats), 0, sizeof(struct net_device_stats));
> +#endif
> +
> + dev->iface = iface;
> + memset(&(dev->meid), '0', 14);
> +
> + DBG("Mac Address:\n");
> + printhex(&dev->usbnet->net->dev_addr[0], 6);
> +
> + dev->valid = false;
> + memset(&dev->qmi, 0, sizeof(struct qmidev));
> +
> + dev->qmi.devclass = devclass;
> +
> + INIT_LIST_HEAD(&dev->qmi.clients);
> + init_completion(&dev->worker.work);
> + spin_lock_init(&dev->qmi.clients_lock);
> +
> + dev->down = 0;
> + qc_setdown(dev, DOWN_NO_NDIS_CONNECTION);
> + qc_setdown(dev, DOWN_NET_IFACE_STOPPED);
> +
> + status = qc_register(dev);
> + if (status) {
> + qc_deregister(dev);
> + }
> +
> + return status;
> +}
> +
> +EXPORT_SYMBOL_GPL(qcnet_probe);
> +
> +static struct usb_driver qcusbnet =
> +{
> + .name = "QCUSBNet2k",
> + .id_table = qc_vidpids,
> + .probe = qcnet_probe,
> + .disconnect = usbnet_disconnect,
> + .suspend = qc_suspend,
> + .resume = qc_resume,
> + .supports_autosuspend = true,
> +};
> +
> +static int __init modinit(void)
> +{
> + devclass = class_create(THIS_MODULE, "QCQMI");
> + if (IS_ERR(devclass)) {
> + DBG("error at class_create %ld\n", PTR_ERR(devclass));
> + return -ENOMEM;
> + }
> + printk(KERN_INFO "%s: %s\n", DRIVER_DESC, DRIVER_VERSION);
> + return usb_register(&qcusbnet);
> +}
> +module_init(modinit);
> +
> +static void __exit modexit(void)
> +{
> + usb_deregister(&qcusbnet);
> + class_destroy(devclass);
> +}
> +module_exit(modexit);
> +
> +MODULE_VERSION(DRIVER_VERSION);
> +MODULE_AUTHOR(DRIVER_AUTHOR);
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_LICENSE("Dual BSD/GPL");
> +
> +module_param(debug, bool, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(debug, "Debuging enabled or not");
> diff --git a/drivers/net/usb/qcusbnet/qcusbnet.h b/drivers/net/usb/qcusbnet/qcusbnet.h
> new file mode 100644
> index 0000000..2f20868
> --- /dev/null
> +++ b/drivers/net/usb/qcusbnet/qcusbnet.h
> @@ -0,0 +1,24 @@
> +/* qcusbnet.h - gobi network device header
> + * Copyright (c) 2010, Code Aurora Forum. 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
> + * only version 2 as published by the Free Software Foundation.
> +
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> +
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> + * 02110-1301, USA.
> + */
> +
> +#ifndef QCUSBNET_QCUSBNET_H
> +#define QCUSBNET_QCUSBNET_H
> +
> +extern int qc_suspend(struct usb_interface *iface, pm_message_t event);
> +
> +#endif /* !QCUSBNET_QCUSBNET_H */
> diff --git a/drivers/net/usb/qcusbnet/qmi.c b/drivers/net/usb/qcusbnet/qmi.c
> new file mode 100755
> index 0000000..2fc8ce8
> --- /dev/null
> +++ b/drivers/net/usb/qcusbnet/qmi.c
> @@ -0,0 +1,358 @@
> +/* qmi.c - QMI protocol implementation
> + * Copyright (c) 2010, Code Aurora Forum. 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
> + * only version 2 as published by the Free Software Foundation.
> +
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> +
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> + * 02110-1301, USA.
> + */
> +
> +#include "qmi.h"
> +
> +#include <linux/slab.h>
> +
> +struct qmux {
> + u8 tf; /* always 1 */
> + u16 len;
> + u8 ctrl;
> + u8 service;
> + u8 qmicid;
> +} __attribute__((__packed__));
> +
> +struct getcid_req {
> + struct qmux header;
> + u8 req;
> + u8 tid;
> + u16 msgid;
> + u16 tlvsize;
> + u8 service;
> + u16 size;
> + u8 qmisvc;
> +} __attribute__((__packed__));
> +
> +struct releasecid_req {
> + struct qmux header;
> + u8 req;
> + u8 tid;
> + u16 msgid;
> + u16 tlvsize;
> + u8 rlscid;
> + u16 size;
> + u16 cid;
> +} __attribute__((__packed__));
> +
> +struct ready_req {
> + struct qmux header;
> + u8 req;
> + u8 tid;
> + u16 msgid;
> + u16 tlvsize;
> +} __attribute__((__packed__));
> +
> +struct seteventreport_req {
> + struct qmux header;
> + u8 req;
> + u16 tid;
> + u16 msgid;
> + u16 tlvsize;
> + u8 reportchanrate;
> + u16 size;
> + u8 period;
> + u32 mask;
> +} __attribute__((__packed__));
> +
> +struct getpkgsrvcstatus_req {
> + struct qmux header;
> + u8 req;
> + u16 tid;
> + u16 msgid;
> + u16 tlvsize;
> +} __attribute__((__packed__));
> +
> +struct getmeid_req {
> + struct qmux header;
> + u8 req;
> + u16 tid;
> + u16 msgid;
> + u16 tlvsize;
> +} __attribute__((__packed__));
> +
> +const size_t qmux_size = sizeof(struct qmux);
> +
> +void *qmictl_new_getcid(u8 tid, u8 svctype, size_t *size)
> +{
> + struct getcid_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
> + if (!req)
> + return NULL;
> + req->req = 0x00;
> + req->tid = tid;
> + req->msgid = 0x0022;
> + req->tlvsize = 0x0004;
> + req->service = 0x01;
> + req->size = 0x0001;
> + req->qmisvc = svctype;
> + *size = sizeof(*req);
> + return req;
> +}
> +
> +void *qmictl_new_releasecid(u8 tid, u16 cid, size_t *size)
> +{
> + struct releasecid_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
> + if (!req)
> + return NULL;
> + req->req = 0x00;
> + req->tid = tid;
> + req->msgid = 0x0023;
> + req->tlvsize = 0x05;
> + req->rlscid = 0x01;
> + req->size = 0x0002;
> + req->cid = cid;
> + *size = sizeof(*req);
> + return req;
> +}
> +
> +void *qmictl_new_ready(u8 tid, size_t *size)
> +{
> + struct ready_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
> + if (!req)
> + return NULL;
> + req->req = 0x00;
> + req->tid = tid;
> + req->msgid = 0x21;
> + req->tlvsize = 0;
> + *size = sizeof(*req);
> + return req;
> +}
> +
> +void *qmiwds_new_seteventreport(u8 tid, size_t *size)
> +{
> + struct seteventreport_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
> + req->req = 0x00;
> + req->tid = tid;
> + req->msgid = 0x0001;
> + req->tlvsize = 0x0008;
> + req->reportchanrate = 0x11;
> + req->size = 0x0005;
> + req->period = 0x01;
> + req->mask = 0x000000ff;
> + *size = sizeof(*req);
> + return req;
> +}
> +
> +void *qmiwds_new_getpkgsrvcstatus(u8 tid, size_t *size)
> +{
> + struct getpkgsrvcstatus_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
> + if (!req)
> + return NULL;
> + req->req = 0x00;
> + req->tid = tid;
> + req->msgid = 0x22;
> + req->tlvsize = 0x0000;
> + *size = sizeof(*req);
> + return req;
> +}
> +
> +void *qmidms_new_getmeid(u8 tid, size_t *size)
> +{
> + struct getmeid_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
> + if (!req)
> + return NULL;
> + req->req = 0x00;
> + req->tid = tid;
> + req->msgid = 0x25;
> + req->tlvsize = 0x0000;
> + *size = sizeof(*req);
> + return req;
> +}
> +
> +int qmux_parse(u16 *cid, void *buf, size_t size)
> +{
> + struct qmux *qmux = buf;
> +
> + if (!buf || size < 12)
> + return -ENOMEM;
> +
> + if (qmux->tf != 1 || qmux->len != size - 1 || qmux->ctrl != 0x80)
> + return -EINVAL;
> +
> + *cid = (qmux->qmicid << 8) + qmux->service;
> + return sizeof(*qmux);
> +}
> +
> +int qmux_fill(u16 cid, void *buf, size_t size)
> +{
> + struct qmux *qmux = buf;
> +
> + if (!buf || size < sizeof(*qmux))
> + return -ENOMEM;
> +
> + qmux->tf = 1;
> + qmux->len = size - 1;
> + qmux->ctrl = 0;
> + qmux->service = cid & 0xff;
> + qmux->qmicid = cid >> 8;
> + return 0;
> +}
> +
> +static u16 tlv_get(void *msg, u16 msgsize, u8 type, void *buf, u16 bufsize) {
> + u16 pos;
> + u16 msize = 0;
> +
> + if (!msg || !buf)
> + return -ENOMEM;
> +
> + for (pos = 4; pos + 3 < msgsize; pos += msize + 3) {
> + msize = *(u16 *)(msg + pos + 1);
> + if (*(u8 *)(msg + pos) == type) {
> + if (bufsize < msize)
> + return -ENOMEM;
> +
> + memcpy(buf, msg + pos + 3, msize);
> + return msize;
> + }
> + }
> +
> + return -ENOMSG;
> +}
> +
> +int qmi_msgisvalid(void *msg, u16 size)
> +{
> + char tlv[4];
> +
> + if (tlv_get(msg, size, 2, &tlv[0], 4) == 4) {
> + if (*(u16 *)&tlv[0] != 0)
> + return *(u16 *)&tlv[2];
> + else
> + return 0;
> + }
> + return -ENOMSG;
> +}
> +
> +int qmi_msgid(void *msg, u16 size)
> +{
> + return size < 2 ? -ENODATA : *(u16 *)msg;
> +}
> +
> +int qmictl_alloccid_resp(void *buf, u16 size, u16 *cid)
> +{
> + int result;
> + u8 offset = sizeof(struct qmux) + 2;
> +
> + if (!buf || size < offset)
> + return -ENOMEM;
> +
> + buf = buf + offset;
> + size -= offset;
> +
> + result = qmi_msgid(buf, size);
> + if (result != 0x22)
> + return -EFAULT;
> +
> + result = qmi_msgisvalid(buf, size);
> + if (result != 0)
> + return -EFAULT;
> +
> + result = tlv_get(buf, size, 0x01, cid, 2);
> + if (result != 2)
> + return -EFAULT;
> +
> + return 0;
> +}
> +
> +int qmictl_freecid_resp(void *buf, u16 size)
> +{
> + int result;
> + u8 offset = sizeof(struct qmux) + 2;
> +
> + if (!buf || size < offset)
> + return -ENOMEM;
> +
> + buf = buf + offset;
> + size -= offset;
> +
> + result = qmi_msgid(buf, size);
> + if (result != 0x23)
> + return -EFAULT;
> +
> + result = qmi_msgisvalid(buf, size);
> + if (result != 0)
> + return -EFAULT;
> +
> + return 0;
> +}
> +
> +int qmiwds_event_resp(void *buf, u16 size, struct qmiwds_stats *stats)
> +{
> + int result;
> + u8 status[2];
> +
> + u8 offset = sizeof(struct qmux) + 3;
> +
> + if (!buf || size < offset || !stats)
> + return -ENOMEM;
> +
> + buf = buf + offset;
> + size -= offset;
> +
> + result = qmi_msgid(buf, size);
> + if (result == 0x01) {
> + tlv_get(buf, size, 0x10, (void*)&stats->txok, 4);
> + tlv_get(buf, size, 0x11, (void*)&stats->rxok, 4);
> + tlv_get(buf, size, 0x12, (void*)&stats->txerr, 4);
> + tlv_get(buf, size, 0x13, (void*)&stats->rxerr, 4);
> + tlv_get(buf, size, 0x14, (void*)&stats->txofl, 4);
> + tlv_get(buf, size, 0x15, (void*)&stats->rxofl, 4);
> + tlv_get(buf, size, 0x19, (void*)&stats->txbytesok, 8);
> + tlv_get(buf, size, 0x1A, (void*)&stats->rxbytesok, 8);
> + } else if (result == 0x22) {
> + result = tlv_get(buf, size, 0x01, &status[0], 2);
> + if (result >= 1)
> + stats->linkstate = status[0] == 0x02;
> + if (result == 2)
> + stats->reconfigure = status[1] == 0x01;
> +
> + if (result < 0)
> + return result;
> + } else {
> + return -EFAULT;
> + }
> +
> + return 0;
> +}
> +
> +int qmidms_meid_resp(void *buf, u16 size, char *meid, int meidsize)
> +{
> + int result;
> +
> + u8 offset = sizeof(struct qmux) + 3;
> +
> + if (!buf || size < offset || meidsize < 14)
> + return -ENOMEM;
> +
> + buf = buf + offset;
> + size -= offset;
> +
> + result = qmi_msgid(buf, size);
> + if (result != 0x25)
> + return -EFAULT;
> +
> + result = qmi_msgisvalid(buf, size);
> + if (result)
> + return -EFAULT;
> +
> + result = tlv_get(buf, size, 0x12, (void*)meid, 14);
> + if (result != 14)
> + return -EFAULT;
> +
> + return 0;
> +}
> +
> diff --git a/drivers/net/usb/qcusbnet/qmi.h b/drivers/net/usb/qcusbnet/qmi.h
> new file mode 100755
> index 0000000..7954790
> --- /dev/null
> +++ b/drivers/net/usb/qcusbnet/qmi.h
> @@ -0,0 +1,67 @@
> +/* qmi.h - QMI protocol header
> + * Copyright (c) 2010, Code Aurora Forum. 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
> + * only version 2 as published by the Free Software Foundation.
> +
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> +
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> + * 02110-1301, USA.
> + */
> +
> +#ifndef QCUSBNET_QMI_H
> +#define QCUSBNET_QMI_H
> +
> +#include <linux/types.h>
> +
> +#define QMICTL 0
> +#define QMIWDS 1
> +#define QMIDMS 2
> +
> +#define true 1
> +#define false 0
> +
> +#define ENOMEM 12
> +#define EFAULT 14
> +#define EINVAL 22
> +#define ENOMSG 42
> +#define ENODATA 61
> +
> +int qmux_parse(u16 *cid, void *buf, size_t size);
> +int qmux_fill(u16 cid, void *buf, size_t size);
> +
> +extern const size_t qmux_size;
> +
> +void *qmictl_new_getcid(u8 tid, u8 svctype, size_t *size);
> +void *qmictl_new_releasecid(u8 tid, u16 cid, size_t *size);
> +void *qmictl_new_ready(u8 tid, size_t *size);
> +void *qmiwds_new_seteventreport(u8 tid, size_t *size);
> +void *qmiwds_new_getpkgsrvcstatus(u8 tid, size_t *size);
> +void *qmidms_new_getmeid(u8 tid, size_t *size);
> +
> +struct qmiwds_stats {
> + u32 txok;
> + u32 rxok;
> + u32 txerr;
> + u32 rxerr;
> + u32 txofl;
> + u32 rxofl;
> + u64 txbytesok;
> + u64 rxbytesok;
> + bool linkstate;
> + bool reconfigure;
> +};
> +
> +int qmictl_alloccid_resp(void *buf, u16 size, u16 *cid);
> +int qmictl_freecid_resp(void *buf, u16 size);
> +int qmiwds_event_resp(void *buf, u16 size, struct qmiwds_stats *stats);
> +int qmidms_meid_resp(void *buf, u16 size, char *meid, int meidsize);
> +
> +#endif /* !QCUSBNET_QMI_H */
> diff --git a/drivers/net/usb/qcusbnet/qmidevice.c b/drivers/net/usb/qcusbnet/qmidevice.c
> new file mode 100755
> index 0000000..b5006be
> --- /dev/null
> +++ b/drivers/net/usb/qcusbnet/qmidevice.c
> @@ -0,0 +1,1621 @@
> +/* qmidevice.c - gobi QMI device
> + * Copyright (c) 2010, Code Aurora Forum. 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
> + * only version 2 as published by the Free Software Foundation.
> +
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> +
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> + * 02110-1301, USA.
> + */
> +
> +#include "qmidevice.h"
> +#include "qcusbnet.h"
> +
> +struct readreq {
> + struct list_head node;
> + void *data;
> + u16 tid;
> + u16 size;
> +};
> +
> +struct notifyreq {
> + struct list_head node;
> + void (* func)(struct qcusbnet *, u16, void *);
> + u16 tid;
> + void *data;
> +};
> +
> +struct client {
> + struct list_head node;
> + u16 cid;
> + struct list_head reads;
> + struct list_head notifies;
> + struct list_head urbs;
> +};
> +
> +struct urbsetup {
> + u8 type;
> + u8 code;
> + u16 value;
> + u16 index;
> + u16 len;
> +};
> +
> +struct qmihandle {
> + u16 cid;
> + struct qcusbnet *dev;
> +};
> +
> +extern int debug;
> +static int qcusbnet2k_fwdelay = 0;
> +
> +static bool device_valid(struct qcusbnet *dev);
> +static struct client *client_bycid(struct qcusbnet *dev, u16 cid);
> +static bool client_addread(struct qcusbnet *dev, u16 cid, u16 tid, void *data, u16 size);
> +static bool client_delread(struct qcusbnet *dev, u16 cid, u16 tid, void **data, u16 *size);
> +static bool client_addnotify(struct qcusbnet *dev, u16 cid, u16 tid,
> + void (*hook)(struct qcusbnet *, u16 cid, void *),
> + void *data);
> +static bool client_notify(struct qcusbnet *dev, u16 cid, u16 tid);
> +static bool client_addurb(struct qcusbnet *dev, u16 cid, struct urb *urb);
> +static struct urb *client_delurb(struct qcusbnet *dev, u16 cid);
> +
> +static int devqmi_open(struct inode *inode, struct file *file);
> +static long devqmi_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
> +static int devqmi_close(struct file *file, fl_owner_t ftable);
> +static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size,
> + loff_t *pos);
> +static ssize_t devqmi_write(struct file *file, const char __user *buf,
> + size_t size, loff_t *pos);
> +
> +static bool qmi_ready(struct qcusbnet *dev, u16 timeout);
> +static void wds_callback(struct qcusbnet *dev, u16 cid, void *data);
> +static int setup_wds_callback(struct qcusbnet *dev);
> +static int qmidms_getmeid(struct qcusbnet *dev);
> +
> +#define IOCTL_QMI_GET_SERVICE_FILE 0x8BE0 + 1
> +#define IOCTL_QMI_GET_DEVICE_VIDPID 0x8BE0 + 2
> +#define IOCTL_QMI_GET_DEVICE_MEID 0x8BE0 + 3
> +#define CDC_GET_ENCAPSULATED_RESPONSE 0x01A1ll
> +#define CDC_CONNECTION_SPEED_CHANGE 0x08000000002AA1ll
> +
> +struct file_operations devqmi_fops =
> +{
> + .owner = THIS_MODULE,
> + .read = devqmi_read,
> + .write = devqmi_write,
> + .unlocked_ioctl = devqmi_ioctl,
> + .open = devqmi_open,
> + .flush = devqmi_close,
> +};
> +
> +#ifdef CONFIG_SMP
> +static inline void assert_locked(struct qcusbnet *dev)
> +{
> + BUG_ON(!spin_is_locked(&dev->qmi.clients_lock));
> +}
> +#else
> +static inline void assert_locked(struct qcusbnet *dev)
> +{
> +
> +}
> +#endif
> +
> +static bool device_valid(struct qcusbnet *dev)
> +{
> + return dev && dev->valid;
> +}
> +
> +void printhex(const void *data, size_t size)
> +{
> + const u8 *cdata = data;
> + char *buf;
> + size_t pos;
> +
> + buf = kmalloc(size * 3 + 1, GFP_ATOMIC);
> + if (!buf) {
> + DBG("Unable to allocate buffer\n");
> + return;
> + }
> +
> + memset(buf, 0 , size * 3 + 1);
> +
> + for (pos = 0; pos < size; pos++) {
> + snprintf(buf + (pos * 3), 4, "%02X ", cdata[pos]);
> + }
> +
> + DBG( " : %s\n", buf);
> +
> + kfree(buf);
> +}
> +
> +void qc_setdown(struct qcusbnet *dev, u8 reason)
> +{
> + set_bit(reason, &dev->down);
> + netif_carrier_off(dev->usbnet->net);
> +}
> +
> +void qc_cleardown(struct qcusbnet *dev, u8 reason)
> +{
> + clear_bit(reason, &dev->down);
> + if (!dev->down)
> + netif_carrier_on(dev->usbnet->net);
> +}
> +
> +bool qc_isdown(struct qcusbnet *dev, u8 reason)
> +{
> + return test_bit(reason, &dev->down);
> +}
> +
> +static void read_callback(struct urb *urb)
> +{
> + struct list_head *node;
> + int result;
> + u16 cid;
> + struct client *client;
> + void *data;
> + void *copy;
> + u16 size;
> + struct qcusbnet *dev;
> + unsigned long flags;
> + u16 tid;
> +
> + if (!urb) {
> + DBG("bad read URB\n");
> + return;
> + }
> +
> + dev = urb->context;
> + if (!device_valid(dev)) {
> + DBG("Invalid device!\n");
> + return;
> + }
> +
> + if (urb->status) {
> + DBG("Read status = %d\n", urb->status);
> + return;
> + }
> +
> + DBG("Read %d bytes\n", urb->actual_length);
> +
> + data = urb->transfer_buffer;
> + size = urb->actual_length;
> +
> + printhex(data, size);
> +
> + result = qmux_parse(&cid, data, size);
> + if (result < 0) {
> + DBG("Read error parsing QMUX %d\n", result);
> + return;
> + }
> +
> + if (size < result + 3) {
> + DBG("Data buffer too small to parse\n");
> + return;
> + }
> +
> + if (cid == QMICTL)
> + tid = *(u8*)(data + result + 1);
> + else
> + tid = *(u16*)(data + result + 1);
> + spin_lock_irqsave(&dev->qmi.clients_lock, flags);
> +
> + list_for_each(node, &dev->qmi.clients) {
> + client = list_entry(node, struct client, node);
> + if (client->cid == cid || (client->cid | 0xff00) == cid) {
> + copy = kmalloc(size, GFP_ATOMIC);
> + memcpy(copy, data, size);
> + if (!client_addread(dev, client->cid, tid, copy, size)) {
> + DBG("Error allocating pReadMemListEntry "
> + "read will be discarded\n");
> + kfree(copy);
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + return;
> + }
> +
> + DBG("Creating new readListEntry for client 0x%04X, TID %x\n",
> + cid, tid);
> +
> + client_notify(dev, client->cid, tid);
> +
> + if (cid >> 8 != 0xff)
> + break;
> + }
> + }
> +
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> +}
> +
> +static void int_callback(struct urb *urb)
> +{
> + int status;
> + int interval;
> + struct qcusbnet *dev = (struct qcusbnet *)urb->context;
> +
> + if (!device_valid(dev)) {
> + DBG("Invalid device!\n");
> + return;
> + }
> +
> + if (urb->status) {
> + DBG("Int status = %d\n", urb->status);
> + if (urb->status != -EOVERFLOW)
> + return;
> + } else {
> + if ((urb->actual_length == 8)
> + && (*(u64*)urb->transfer_buffer == CDC_GET_ENCAPSULATED_RESPONSE)) {
> + usb_fill_control_urb(dev->qmi.readurb, dev->usbnet->udev,
> + usb_rcvctrlpipe(dev->usbnet->udev, 0),
> + (unsigned char *)dev->qmi.readsetup,
> + dev->qmi.readbuf,
> + DEFAULT_READ_URB_LENGTH,
> + read_callback, dev);
> + status = usb_submit_urb(dev->qmi.readurb, GFP_ATOMIC);
> + if (status) {
> + DBG("Error submitting Read URB %d\n", status);
> + return;
> + }
> + } else if ((urb->actual_length == 16)
> + && (*(u64*)urb->transfer_buffer == CDC_CONNECTION_SPEED_CHANGE)) {
> + /* if upstream or downstream is 0, stop traffic.
> + * Otherwise resume it */
> + if ((*(u32*)(urb->transfer_buffer + 8) == 0)
> + || (*(u32*)(urb->transfer_buffer + 12) == 0)) {
> + qc_setdown(dev, DOWN_CDC_CONNECTION_SPEED);
> + DBG("traffic stopping due to CONNECTION_SPEED_CHANGE\n");
> + } else {
> + qc_cleardown(dev, DOWN_CDC_CONNECTION_SPEED);
> + DBG("resuming traffic due to CONNECTION_SPEED_CHANGE\n");
> + }
> + } else {
> + DBG("ignoring invalid interrupt in packet\n");
> + printhex(urb->transfer_buffer, urb->actual_length);
> + }
> + }
> +
> + interval = (dev->usbnet->udev->speed == USB_SPEED_HIGH) ? 7 : 3;
> +
> + usb_fill_int_urb(urb, urb->dev, urb->pipe, urb->transfer_buffer,
> + urb->transfer_buffer_length, urb->complete,
> + urb->context, interval);
> + status = usb_submit_urb(urb, GFP_ATOMIC);
> + if (status)
> + DBG("Error re-submitting Int URB %d\n", status);
> + return;
> +}
> +
> +int qc_startread(struct qcusbnet *dev)
> +{
> + int interval;
> +
> + if (!device_valid(dev)) {
> + DBG("Invalid device!\n");
> + return -ENXIO;
> + }
> +
> + dev->qmi.readurb = usb_alloc_urb(0, GFP_KERNEL);
> + if (!dev->qmi.readurb) {
> + DBG("Error allocating read urb\n");
> + return -ENOMEM;
> + }
> +
> + dev->qmi.inturb = usb_alloc_urb(0, GFP_KERNEL);
> + if (!dev->qmi.inturb) {
> + usb_free_urb(dev->qmi.readurb);
> + DBG("Error allocating int urb\n");
> + return -ENOMEM;
> + }
> +
> + dev->qmi.readbuf = kmalloc(DEFAULT_READ_URB_LENGTH, GFP_KERNEL);
> + if (!dev->qmi.readbuf) {
> + usb_free_urb(dev->qmi.readurb);
> + usb_free_urb(dev->qmi.inturb);
> + DBG("Error allocating read buffer\n");
> + return -ENOMEM;
> + }
> +
> + dev->qmi.intbuf = kmalloc(DEFAULT_READ_URB_LENGTH, GFP_KERNEL);
> + if (!dev->qmi.intbuf) {
> + usb_free_urb(dev->qmi.readurb);
> + usb_free_urb(dev->qmi.inturb);
> + kfree(dev->qmi.readbuf);
> + DBG("Error allocating int buffer\n");
> + return -ENOMEM;
> + }
> +
> + dev->qmi.readsetup = kmalloc(sizeof(*dev->qmi.readsetup), GFP_KERNEL);
> + if (!dev->qmi.readsetup) {
> + usb_free_urb(dev->qmi.readurb);
> + usb_free_urb(dev->qmi.inturb);
> + kfree(dev->qmi.readbuf);
> + kfree(dev->qmi.intbuf);
> + DBG("Error allocating setup packet buffer\n");
> + return -ENOMEM;
> + }
> +
> + dev->qmi.readsetup->type = 0xA1;
> + dev->qmi.readsetup->code = 1;
> + dev->qmi.readsetup->value = 0;
> + dev->qmi.readsetup->index = 0;
> + dev->qmi.readsetup->len = DEFAULT_READ_URB_LENGTH;
> +
> + interval = (dev->usbnet->udev->speed == USB_SPEED_HIGH) ? 7 : 3;
> +
> + usb_fill_int_urb(dev->qmi.inturb, dev->usbnet->udev,
> + usb_rcvintpipe(dev->usbnet->udev, 0x81),
> + dev->qmi.intbuf, DEFAULT_READ_URB_LENGTH,
> + int_callback, dev, interval);
> + return usb_submit_urb(dev->qmi.inturb, GFP_KERNEL);
> +}
> +
> +void qc_stopread(struct qcusbnet * dev)
> +{
> + if (dev->qmi.readurb) {
> + DBG("Killng read URB\n");
> + usb_kill_urb(dev->qmi.readurb);
> + }
> +
> + if (dev->qmi.inturb) {
> + DBG("Killng int URB\n");
> + usb_kill_urb(dev->qmi.inturb);
> + }
> +
> + kfree(dev->qmi.readsetup);
> + dev->qmi.readsetup = NULL;
> + kfree(dev->qmi.readbuf);
> + dev->qmi.readbuf = NULL;
> + kfree(dev->qmi.intbuf);
> + dev->qmi.intbuf = NULL;
> +
> + usb_free_urb(dev->qmi.readurb);
> + dev->qmi.readurb = NULL;
> + usb_free_urb(dev->qmi.inturb);
> + dev->qmi.inturb = NULL;
> +}
> +
> +static int read_async(struct qcusbnet *dev, u16 cid, u16 tid,
> + void (*hook)(struct qcusbnet*, u16, void *), void *data)
> +{
> + struct list_head *node;
> + struct client *client;
> + struct readreq *readreq;
> +
> + unsigned long flags;
> +
> + if (!device_valid(dev)) {
> + DBG("Invalid device!\n");
> + return -ENXIO;
> + }
> +
> + spin_lock_irqsave(&dev->qmi.clients_lock, flags);
> +
> + client = client_bycid(dev, cid);
> + if (!client) {
> + DBG("Could not find matching client ID 0x%04X\n", cid);
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + return -ENXIO;
> + }
> +
> + list_for_each(node, &client->reads) {
> + readreq = list_entry(node, struct readreq, node);
> + if (!tid || tid == readreq->tid) {
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + hook(dev, cid, data);
> + return 0;
> + }
> + }
> +
> + if (!client_addnotify(dev, cid, tid, hook, data))
> + DBG("Unable to register for notification\n");
> +
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + return 0;
> +}
> +
> +static void upsem(struct qcusbnet *dev, u16 cid, void *data)
> +{
> + DBG("0x%04X\n", cid);
> + up((struct semaphore *)data);
> +}
> +
> +static int read_sync(struct qcusbnet *dev, void **buf, u16 cid, u16 tid)
> +{
> + struct list_head *node;
> + int result;
> + struct client * client;
> + struct notifyreq *notify;
> + struct semaphore sem;
> + void * data;
> + unsigned long flags;
> + u16 size;
> +
> + if (!device_valid(dev)) {
> + DBG("Invalid device!\n");
> + return -ENXIO;
> + }
> +
> + spin_lock_irqsave(&dev->qmi.clients_lock, flags);
> +
> + client = client_bycid(dev, cid);
> + if (!client) {
> + DBG("Could not find matching client ID 0x%04X\n", cid);
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + return -ENXIO;
> + }
> +
> + while (!client_delread(dev, cid, tid, &data, &size)) {
> + sema_init(&sem, 0);
> + if (!client_addnotify(dev, cid, tid, upsem, &sem)) {
> + DBG("unable to register for notification\n");
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + return -EFAULT;
> + }
> +
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> +
> + result = down_interruptible(&sem);
> + if (result) {
> + DBG("Interrupted %d\n", result);
> + spin_lock_irqsave(&dev->qmi.clients_lock, flags);
> + list_for_each(node, &client->notifies) {
> + notify = list_entry(node, struct notifyreq, node);
> + if (notify->data == &sem) {
> + list_del(¬ify->node);
> + kfree(notify);
> + break;
> + }
> + }
> +
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + return -EINTR;
> + }
> +
> + if (!device_valid(dev)) {
> + DBG("Invalid device!\n");
> + return -ENXIO;
> + }
> +
> + spin_lock_irqsave(&dev->qmi.clients_lock, flags);
> + }
> +
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + *buf = data;
> + return size;
> +}
> +
> +static void write_callback(struct urb *urb)
> +{
> + if (!urb) {
> + DBG("null urb\n");
> + return;
> + }
> +
> + DBG("Write status/size %d/%d\n", urb->status, urb->actual_length);
> + up((struct semaphore *)urb->context);
> +}
> +
> +static int write_sync(struct qcusbnet *dev, char *buf, int size, u16 cid)
> +{
> + int result;
> + struct semaphore sem;
> + struct urb *urb;
> + struct urbsetup setup;
> + unsigned long flags;
> +
> + if (!device_valid(dev)) {
> + DBG("Invalid device!\n");
> + return -ENXIO;
> + }
> +
> + urb = usb_alloc_urb(0, GFP_KERNEL);
> + if (!urb) {
> + DBG("URB mem error\n");
> + return -ENOMEM;
> + }
> +
> + result = qmux_fill(cid, buf, size);
> + if (result < 0) {
> + usb_free_urb(urb);
> + return result;
> + }
> +
> + /* CDC Send Encapsulated Request packet */
> + setup.type = 0x21;
> + setup.code = 0;
> + setup.value = 0;
> + setup.index = 0;
> + setup.len = 0;
> + setup.len = size;
> +
> + usb_fill_control_urb(urb, dev->usbnet->udev,
> + usb_sndctrlpipe(dev->usbnet->udev, 0),
> + (unsigned char *)&setup, (void*)buf, size,
> + NULL, dev);
> +
> + DBG("Actual Write:\n");
> + printhex(buf, size);
> +
> + sema_init(&sem, 0);
> +
> + urb->complete = write_callback;
> + urb->context = &sem;
> +
> + result = usb_autopm_get_interface(dev->iface);
> + if (result < 0) {
> + DBG("unable to resume interface: %d\n", result);
> + if (result == -EPERM) {
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
> + dev->usbnet->udev->auto_pm = 0;
> +#endif
> + qc_suspend(dev->iface, PMSG_SUSPEND);
> + }
> + return result;
> + }
> +
> + spin_lock_irqsave(&dev->qmi.clients_lock, flags);
> +
> + if (!client_addurb(dev, cid, urb)) {
> + usb_free_urb(urb);
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + usb_autopm_put_interface(dev->iface);
> + return -EINVAL;
> + }
> +
> + result = usb_submit_urb(urb, GFP_KERNEL);
> + if (result < 0) {
> + DBG("submit URB error %d\n", result);
> + if (client_delurb(dev, cid) != urb) {
> + DBG("Didn't get write URB back\n");
> + }
> +
> + usb_free_urb(urb);
> +
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + usb_autopm_put_interface(dev->iface);
> + return result;
> + }
> +
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + result = down_interruptible(&sem);
> + if (!device_valid(dev)) {
> + DBG("Invalid device!\n");
> + return -ENXIO;
> + }
> +
> + usb_autopm_put_interface(dev->iface);
> + spin_lock_irqsave(&dev->qmi.clients_lock, flags);
> + if (client_delurb(dev, cid) != urb)
> + {
> + DBG("Didn't get write URB back\n");
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + return -EINVAL;
> + }
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> +
> + if (!result) {
> + if (!urb->status) {
> + result = size;
> + } else {
> + DBG("bad status = %d\n", urb->status);
> + result = urb->status;
> + }
> + } else {
> + DBG("Interrupted %d !!!\n", result);
> + DBG("Device may be in bad state and need reset !!!\n");
> + usb_kill_urb(urb);
> + }
> +
> + usb_free_urb(urb);
> + return result;
> +}
> +
> +static int client_alloc(struct qcusbnet *dev, u8 type)
> +{
> + u16 cid;
> + struct client *client;
> + int result;
> + void * wbuf;
> + size_t wbufsize;
> + void * rbuf;
> + u16 rbufsize;
> + unsigned long flags;
> + u8 tid;
> +
> + if (!device_valid(dev)) {
> + DBG("Invalid device!\n");
> + return -ENXIO;
> + }
> +
> + if (type) {
> + tid = atomic_add_return(1, &dev->qmi.qmitid);
> + if (!tid)
> + atomic_add_return(1, &dev->qmi.qmitid);
> + wbuf = qmictl_new_getcid(tid, type, &wbufsize);
> + if (!wbuf)
> + return -ENOMEM;
> + result = write_sync(dev, wbuf, wbufsize, QMICTL);
> + kfree(wbuf);
> +
> + if (result < 0)
> + return result;
> +
> + result = read_sync(dev, &rbuf, QMICTL, tid);
> + if (result < 0) {
> + DBG("bad read data %d\n", result);
> + return result;
> + }
> + rbufsize = result;
> +
> + result = qmictl_alloccid_resp(rbuf, rbufsize, &cid);
> + kfree(rbuf);
> +
> + if (result < 0)
> + return result;
> + } else {
> + cid = 0;
> + }
> +
> + spin_lock_irqsave(&dev->qmi.clients_lock, flags);
> + if (client_bycid(dev, cid)) {
> + DBG("Client memory already exists\n");
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + return -ETOOMANYREFS;
> + }
> +
> + client = kmalloc(sizeof(*client), GFP_ATOMIC);
> + if (!client) {
> + DBG("Error allocating read list\n");
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + return -ENOMEM;
> + }
> +
> + list_add_tail(&client->node, &dev->qmi.clients);
> + client->cid = cid;
> + INIT_LIST_HEAD(&client->reads);
> + INIT_LIST_HEAD(&client->notifies);
> + INIT_LIST_HEAD(&client->urbs);
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + return cid;
> +}
> +
> +static void client_free(struct qcusbnet *dev, u16 cid)
> +{
> + struct list_head *node, *tmp;
> + int result;
> + struct client *client;
> + struct urb * urb;
> + void * data;
> + u16 size;
> + void * wbuf;
> + size_t wbufsize;
> + void * rbuf;
> + u16 rbufsize;
> + unsigned long flags;
> + u8 tid;
> +
> + if (!device_valid(dev)) {
> + DBG("invalid device\n");
> + return;
> + }
> +
> + DBG("releasing 0x%04X\n", cid);
> +
> + if (cid != QMICTL)
> + {
> + tid = atomic_add_return(1, &dev->qmi.qmitid);
> + if (!tid)
> + tid = atomic_add_return(1, &dev->qmi.qmitid);
> + wbuf = qmictl_new_releasecid(tid, cid, &wbufsize);
> + if (!wbuf) {
> + DBG("memory error\n");
> + } else {
> + result = write_sync(dev, wbuf, wbufsize, QMICTL);
> + kfree(wbuf);
> +
> + if (result < 0) {
> + DBG("bad write status %d\n", result);
> + } else {
> + result = read_sync(dev, &rbuf, QMICTL, tid);
> + if (result < 0) {
> + DBG("bad read status %d\n", result);
> + } else {
> + rbufsize = result;
> + result = qmictl_freecid_resp(rbuf, rbufsize);
> + kfree(rbuf);
> + if (result < 0)
> + DBG("error %d parsing response\n", result);
> + }
> + }
> + }
> + }
> +
> + spin_lock_irqsave(&dev->qmi.clients_lock, flags);
> + list_for_each_safe(node, tmp, &dev->qmi.clients) {
> + client = list_entry(node, struct client, node);
> + if (client->cid == cid) {
> + while (client_notify(dev, cid, 0));
> +
> + urb = client_delurb(dev, cid);
> + while (urb != NULL) {
> + usb_kill_urb(urb);
> + usb_free_urb(urb);
> + urb = client_delurb(dev, cid);
> + }
> +
> + while (client_delread(dev, cid, 0, &data, &size))
> + kfree(data);
> +
> + list_del(&client->node);
> + kfree(client);
> + }
> + }
> +
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> +}
> +
> +struct client *client_bycid(struct qcusbnet *dev, u16 cid)
> +{
> + struct list_head *node;
> + struct client *client;
> +
> + if (!device_valid(dev)) {
> + DBG("Invalid device\n");
> + return NULL;
> + }
> +
> + assert_locked(dev);
> +
> + list_for_each(node, &dev->qmi.clients) {
> + client = list_entry(node, struct client, node);
> + if (client->cid == cid)
> + return client;
> + }
> +
> + DBG("Could not find client mem 0x%04X\n", cid);
> + return NULL;
> +}
> +
> +static bool client_addread(struct qcusbnet *dev, u16 cid, u16 tid, void *data,
> + u16 size)
> +{
> + struct client *client;
> + struct readreq *req;
> +
> + assert_locked(dev);
> +
> + client = client_bycid(dev, cid);
> + if (!client) {
> + DBG("Could not find this client's memory 0x%04X\n", cid);
> + return false;
> + }
> +
> + req = kmalloc(sizeof(*req), GFP_ATOMIC);
> + if (!req) {
> + DBG("Mem error\n");
> + return false;
> + }
> +
> + req->data = data;
> + req->size = size;
> + req->tid = tid;
> +
> + list_add_tail(&req->node, &client->reads);
> +
> + return true;
> +}
> +
> +static bool client_delread(struct qcusbnet *dev, u16 cid, u16 tid, void **data,
> + u16 *size)
> +{
> + struct client *client;
> + struct readreq *req;
> + struct list_head *node;
> +
> + assert_locked(dev);
> +
> + client = client_bycid(dev, cid);
> + if (!client) {
> + DBG("Could not find this client's memory 0x%04X\n", cid);
> + return false;
> + }
> +
> + list_for_each(node, &client->reads) {
> + req = list_entry(node, struct readreq, node);
> + if (!tid || tid == req->tid) {
> + *data = req->data;
> + *size = req->size;
> + list_del(&req->node);
> + kfree(req);
> + return true;
> + }
> +
> + DBG("skipping 0x%04X data TID = %x\n", cid, req->tid);
> + }
> +
> + DBG("No read memory to pop, Client 0x%04X, TID = %x\n", cid, tid);
> + return false;
> +}
> +
> +static bool client_addnotify(struct qcusbnet *dev, u16 cid, u16 tid,
> + void (*hook)(struct qcusbnet *, u16, void *),
> + void *data)
> +{
> + struct client *client;
> + struct notifyreq *req;
> +
> + assert_locked(dev);
> +
> + client = client_bycid(dev, cid);
> + if (!client) {
> + DBG("Could not find this client's memory 0x%04X\n", cid);
> + return false;
> + }
> +
> + req = kmalloc(sizeof(*req), GFP_ATOMIC);
> + if (!req) {
> + DBG("Mem error\n");
> + return false;
> + }
> +
> + list_add_tail(&req->node, &client->notifies);
> + req->func = hook;
> + req->data = data;
> + req->tid = tid;
> +
> + return true;
> +}
> +
> +static bool client_notify(struct qcusbnet *dev, u16 cid, u16 tid)
> +{
> + struct client *client;
> + struct notifyreq *delnotify, *notify;
> + struct list_head *node;
> +
> + assert_locked(dev);
> +
> + client = client_bycid(dev, cid);
> + if (!client) {
> + DBG("Could not find this client's memory 0x%04X\n", cid);
> + return false;
> + }
> +
> + delnotify = NULL;
> +
> + list_for_each(node, &client->notifies) {
> + notify = list_entry(node, struct notifyreq, node);
> + if (!tid || !notify->tid || tid == notify->tid) {
> + delnotify = notify;
> + break;
> + }
> +
> + DBG("skipping data TID = %x\n", notify->tid);
> + }
> +
> + if (delnotify) {
> + list_del(&delnotify->node);
> + if (delnotify->func) {
> + spin_unlock(&dev->qmi.clients_lock);
> + delnotify->func(dev, cid, delnotify->data);
> + spin_lock(&dev->qmi.clients_lock);
> + }
> + kfree(delnotify);
> + return true;
> + }
> +
> + DBG("no one to notify for TID %x\n", tid);
> + return false;
> +}
> +
> +static bool client_addurb(struct qcusbnet *dev, u16 cid, struct urb *urb)
> +{
> + struct client *client;
> + struct urbreq *req;
> +
> + assert_locked(dev);
> +
> + client = client_bycid(dev, cid);
> + if (!client) {
> + DBG("Could not find this client's memory 0x%04X\n", cid);
> + return false;
> + }
> +
> + req = kmalloc(sizeof(*req), GFP_ATOMIC);
> + if (!req) {
> + DBG("Mem error\n");
> + return false;
> + }
> +
> + req->urb = urb;
> + list_add_tail(&req->node, &client->urbs);
> +
> + return true;
> +}
> +
> +static struct urb *client_delurb(struct qcusbnet *dev, u16 cid)
> +{
> + struct client *client;
> + struct urbreq *req;
> + struct urb *urb;
> +
> + assert_locked(dev);
> +
> + client = client_bycid(dev, cid);
> + if (!client) {
> + DBG("Could not find this client's memory 0x%04X\n", cid);
> + return NULL;
> + }
> +
> + if (list_empty(&client->urbs)) {
> + DBG("No URB's to pop\n");
> + return NULL;
> + }
> +
> + req = list_first_entry(&client->urbs, struct urbreq, node);
> + list_del(&req->node);
> + urb = req->urb;
> + kfree(req);
> + return urb;
> +}
> +
> +static int devqmi_open(struct inode *inode, struct file *file)
> +{
> + struct qmihandle * handle;
> + struct qmidev *qmidev = container_of(inode->i_cdev, struct qmidev, cdev);
> + struct qcusbnet * dev = container_of(qmidev, struct qcusbnet, qmi);
> +
> + if (!device_valid(dev)) {
> + DBG("Invalid device\n");
> + return -ENXIO;
> + }
> +
> + file->private_data = kmalloc(sizeof(struct qmihandle), GFP_KERNEL);
> + if (!file->private_data) {
> + DBG("Mem error\n");
> + return -ENOMEM;
> + }
> +
> + handle = (struct qmihandle *)file->private_data;
> + handle->cid = (u16)-1;
> + handle->dev = dev;
> +
> + return 0;
> +}
> +
> +static long devqmi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> +{
> + int result;
> + u32 vidpid;
> +
> + struct qmihandle *handle = (struct qmihandle *)file->private_data;
> +
> + if (!handle) {
> + DBG("Bad file data\n");
> + return -EBADF;
> + }
> +
> + if (!device_valid(handle->dev)) {
> + DBG("Invalid device! Updating f_ops\n");
> + file->f_op = file->f_dentry->d_inode->i_fop;
> + return -ENXIO;
> + }
> +
> + switch (cmd) {
> + case IOCTL_QMI_GET_SERVICE_FILE:
> +
> + DBG("Setting up QMI for service %lu\n", arg);
> + if (!(u8)arg) {
> + DBG("Cannot use QMICTL from userspace\n");
> + return -EINVAL;
> + }
> +
> + if (handle->cid != (u16)-1) {
> + DBG("Close the current connection before opening a new one\n");
> + return -EBADR;
> + }
> +
> + result = client_alloc(handle->dev, (u8)arg);
> + if (result < 0)
> + return result;
> + handle->cid = result;
> +
> + return 0;
> + break;
> +
> +
> + case IOCTL_QMI_GET_DEVICE_VIDPID:
> + if (!arg) {
> + DBG("Bad VIDPID buffer\n");
> + return -EINVAL;
> + }
> +
> + if (!handle->dev->usbnet) {
> + DBG("Bad usbnet\n");
> + return -ENOMEM;
> + }
> +
> + if (!handle->dev->usbnet->udev) {
> + DBG("Bad udev\n");
> + return -ENOMEM;
> + }
> +
> + vidpid = ((le16_to_cpu(handle->dev->usbnet->udev->descriptor.idVendor) << 16)
> + + le16_to_cpu(handle->dev->usbnet->udev->descriptor.idProduct));
> +
> + result = copy_to_user((unsigned int *)arg, &vidpid, 4);
> + if (result)
> + DBG("Copy to userspace failure\n");
> +
> + return result;
> + break;
> +
> + case IOCTL_QMI_GET_DEVICE_MEID:
> + if (!arg) {
> + DBG("Bad MEID buffer\n");
> + return -EINVAL;
> + }
> +
> + result = copy_to_user((unsigned int *)arg, &handle->dev->meid[0], 14);
> + if (result)
> + DBG("copy to userspace failure\n");
> +
> + return result;
> + break;
> + default:
> + return -EBADRQC;
> + }
> +}
> +
> +static int devqmi_close(struct file *file, fl_owner_t ftable)
> +{
> + struct qmihandle * handle = (struct qmihandle *)file->private_data;
> + struct list_head * tasks;
> + struct task_struct * task;
> + struct fdtable * fdtable;
> + int count = 0;
> + int used = 0;
> + unsigned long flags;
> +
> + if (!handle) {
> + DBG("bad file data\n");
> + return -EBADF;
> + }
> +
> + if (file_count(file) != 1) {
> + /* XXX: This can't possibly be safe. We don't hold any sort of
> + * lock here, and we're walking a list of threads... */
> + list_for_each(tasks, ¤t->group_leader->tasks) {
> + task = container_of(tasks, struct task_struct, tasks);
> + if (!task || !task->files)
> + continue;
> + spin_lock_irqsave(&task->files->file_lock, flags);
> + fdtable = files_fdtable(task->files);
> + for (count = 0; count < fdtable->max_fds; count++) {
> + /* Before this function was called, this file was removed
> + * from our task's file table so if we find it in a file
> + * table then it is being used by another task
> + */
> + if (fdtable->fd[count] == file) {
> + used++;
> + break;
> + }
> + }
> + spin_unlock_irqrestore(&task->files->file_lock, flags);
> + }
> +
> + if (used > 0) {
> + DBG("not closing, as this FD is open by %d other process\n", used);
> + return 0;
> + }
> + }
> +
> + if (!device_valid(handle->dev)) {
> + DBG("Invalid device! Updating f_ops\n");
> + file->f_op = file->f_dentry->d_inode->i_fop;
> + return -ENXIO;
> + }
> +
> + DBG("0x%04X\n", handle->cid);
> +
> + file->private_data = NULL;
> +
> + if (handle->cid != (u16)-1)
> + client_free(handle->dev, handle->cid);
> +
> + kfree(handle);
> + return 0;
> +}
> +
> +static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size,
> + loff_t *pos)
> +{
> + int result;
> + void * data = NULL;
> + void * smalldata;
> + struct qmihandle * handle = (struct qmihandle *)file->private_data;
> +
> + if (!handle) {
> + DBG("Bad file data\n");
> + return -EBADF;
> + }
> +
> + if (!device_valid(handle->dev)) {
> + DBG("Invalid device! Updating f_ops\n");
> + file->f_op = file->f_dentry->d_inode->i_fop;
> + return -ENXIO;
> + }
> +
> + if (handle->cid == (u16)-1) {
> + DBG("Client ID must be set before reading 0x%04X\n",
> + handle->cid);
> + return -EBADR;
> + }
> +
> + result = read_sync(handle->dev, &data, handle->cid, 0);
> + if (result <= 0)
> + return result;
> +
> + result -= qmux_size;
> + smalldata = data + qmux_size;
> +
> + if (result > size) {
> + DBG("Read data is too large for amount user has requested\n");
> + kfree(data);
> + return -EOVERFLOW;
> + }
> +
> + if (copy_to_user(buf, smalldata, result)) {
> + DBG("Error copying read data to user\n");
> + result = -EFAULT;
> + }
> +
> + kfree(data);
> + return result;
> +}
> +
> +static ssize_t devqmi_write (struct file *file, const char __user * buf,
> + size_t size, loff_t *pos)
> +{
> + int status;
> + void *wbuf;
> + struct qmihandle *handle = (struct qmihandle *)file->private_data;
> +
> + if (!handle) {
> + DBG("Bad file data\n");
> + return -EBADF;
> + }
> +
> + if (!device_valid(handle->dev)) {
> + DBG("Invalid device! Updating f_ops\n");
> + file->f_op = file->f_dentry->d_inode->i_fop;
> + return -ENXIO;
> + }
> +
> + if (handle->cid == (u16)-1) {
> + DBG("Client ID must be set before writing 0x%04X\n",
> + handle->cid);
> + return -EBADR;
> + }
> +
> + wbuf = kmalloc(size + qmux_size, GFP_KERNEL);
> + if (!wbuf)
> + return -ENOMEM;
> + status = copy_from_user(wbuf + qmux_size, buf, size);
> + if (status) {
> + DBG("Unable to copy data from userspace %d\n", status);
> + kfree(wbuf);
> + return status;
> + }
> +
> + status = write_sync(handle->dev, wbuf, size + qmux_size,
> + handle->cid);
> +
> + kfree(wbuf);
> + if (status == size + qmux_size)
> + return size;
> + return status;
> +}
> +
> +int qc_register(struct qcusbnet *dev)
> +{
> + int result;
> + int qmiidx = 0;
> + dev_t devno;
> + char * name;
> +
> + dev->valid = true;
> + result = client_alloc(dev, QMICTL);
> + if (result) {
> + dev->valid = false;
> + return result;
> + }
> + atomic_set(&dev->qmi.qmitid, 1);
> +
> + result = qc_startread(dev);
> + if (result) {
> + dev->valid = false;
> + return result;
> + }
> +
> + if (!qmi_ready(dev, 30000)) {
> + DBG("Device unresponsive to QMI\n");
> + return -ETIMEDOUT;
> + }
> +
> + result = setup_wds_callback(dev);
> + if (result) {
> + dev->valid = false;
> + return result;
> + }
> +
> + result = qmidms_getmeid(dev);
> + if (result) {
> + dev->valid = false;
> + return result;
> + }
> +
> + result = alloc_chrdev_region(&devno, 0, 1, "qcqmi");
> + if (result < 0)
> + return result;
> +
> + cdev_init(&dev->qmi.cdev, &devqmi_fops);
> + dev->qmi.cdev.owner = THIS_MODULE;
> + dev->qmi.cdev.ops = &devqmi_fops;
> +
> + result = cdev_add(&dev->qmi.cdev, devno, 1);
> + if (result) {
> + DBG("error adding cdev\n");
> + return result;
> + }
> +
> + name = strstr(dev->usbnet->net->name, "usb");
> + if (!name) {
> + DBG("Bad net name: %s\n", dev->usbnet->net->name);
> + return -ENXIO;
> + }
> + name += strlen("usb");
> + qmiidx = simple_strtoul(name, NULL, 10);
> + if (qmiidx < 0) {
> + DBG("Bad minor number\n");
> + return -ENXIO;
> + }
> +
> + printk(KERN_INFO "creating qcqmi%d\n", qmiidx);
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
> + device_create(dev->qmi.devclass, NULL, devno, NULL, "qcqmi%d", qmiidx);
> +#else
> + device_create(dev->qmi.devclass, NULL, devno, "qcqmi%d", qmiidx);
> +#endif
> +
> + dev->qmi.devnum = devno;
> + return 0;
> +}
> +
> +void qc_deregister(struct qcusbnet *dev)
> +{
> + struct list_head *node;
> + struct client *client;
> + struct inode * inode;
> + struct list_head * inodes;
> + struct list_head * tasks;
> + struct task_struct * task;
> + struct fdtable * fdtable;
> + struct file * file;
> + unsigned long flags;
> + int count = 0;
> +
> + if (!device_valid(dev)) {
> + DBG("wrong device\n");
> + return;
> + }
> +
> + list_for_each(node, &dev->qmi.clients) {
> + client = list_entry(node, struct client, node);
> + DBG("release 0x%04X\n", client->cid);
> + client_free(dev, client->cid);
> + }
> +
> + qc_stopread(dev);
> + dev->valid = false;
> + list_for_each(inodes, &dev->qmi.cdev.list) {
> + inode = container_of(inodes, struct inode, i_devices);
> + if (inode != NULL && !IS_ERR(inode)) {
> + list_for_each(tasks, ¤t->group_leader->tasks) {
> + task = container_of(tasks, struct task_struct, tasks);
> + if (!task || !task->files)
> + continue;
> + spin_lock_irqsave(&task->files->file_lock, flags);
> + fdtable = files_fdtable(task->files);
> + for (count = 0; count < fdtable->max_fds; count++) {
> + file = fdtable->fd[count];
> + if (file != NULL && file->f_dentry != NULL) {
> + if (file->f_dentry->d_inode == inode) {
> + rcu_assign_pointer(fdtable->fd[count], NULL);
> + spin_unlock_irqrestore(&task->files->file_lock, flags);
> + DBG("forcing close of open file handle\n");
> + filp_close(file, task->files);
> + spin_lock_irqsave(&task->files->file_lock, flags);
> + }
> + }
> + }
> + spin_unlock_irqrestore(&task->files->file_lock, flags);
> + }
> + }
> + }
> +
> + if (!IS_ERR(dev->qmi.devclass))
> + device_destroy(dev->qmi.devclass, dev->qmi.devnum);
> + cdev_del(&dev->qmi.cdev);
> + unregister_chrdev_region(dev->qmi.devnum, 1);
> +}
> +
> +static bool qmi_ready(struct qcusbnet *dev, u16 timeout)
> +{
> + int result;
> + void * wbuf;
> + size_t wbufsize;
> + void * rbuf;
> + u16 rbufsize;
> + struct semaphore sem;
> + u16 now;
> + unsigned long flags;
> + u8 tid;
> +
> + if (!device_valid(dev)) {
> + DBG("Invalid device\n");
> + return -EFAULT;
> + }
> +
> +
> + for (now = 0; now < timeout; now += 100) {
> + sema_init(&sem, 0);
> +
> + tid = atomic_add_return(1, &dev->qmi.qmitid);
> + if (!tid)
> + tid = atomic_add_return(1, &dev->qmi.qmitid);
> + if (wbuf)
> + kfree(wbuf);
> + wbuf = qmictl_new_ready(tid, &wbufsize);
> + if (!wbuf)
> + return -ENOMEM;
> +
> + result = read_async(dev, QMICTL, tid, upsem, &sem);
> + if (result) {
> + kfree(wbuf);
> + return false;
> + }
> +
> + write_sync(dev, wbuf, wbufsize, QMICTL);
> +
> + msleep(100);
> + if (!down_trylock(&sem)) {
> + spin_lock_irqsave(&dev->qmi.clients_lock, flags);
> + if (client_delread(dev, QMICTL, tid, &rbuf, &rbufsize)) {
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + kfree(rbuf);
> + break;
> + }
> + } else {
> + spin_lock_irqsave(&dev->qmi.clients_lock, flags);
> + client_notify(dev, QMICTL, tid);
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> + }
> + }
> +
> + if (wbuf)
> + kfree(wbuf);
> +
> + if (now >= timeout)
> + return false;
> +
> + DBG("QMI Ready after %u milliseconds\n", now);
> +
> + /* 3580 and newer doesn't need a delay; older needs 5000ms */
> + if (qcusbnet2k_fwdelay)
> + msleep(qcusbnet2k_fwdelay * 1000);
> +
> + return true;
> +}
> +
> +static void wds_callback(struct qcusbnet *dev, u16 cid, void *data)
> +{
> + bool ret;
> + int result;
> + void * rbuf;
> + u16 rbufsize;
> +
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)
> + struct net_device_stats * stats = &(dev->usbnet->stats);
> +#else
> + struct net_device_stats * stats = &(dev->usbnet->net->stats);
> +#endif
> +
> + struct qmiwds_stats dstats = {
> + .txok = (u32)-1,
> + .rxok = (u32)-1,
> + .txerr = (u32)-1,
> + .rxerr = (u32)-1,
> + .txofl = (u32)-1,
> + .rxofl = (u32)-1,
> + .txbytesok = (u64)-1,
> + .rxbytesok = (u64)-1,
> + };
> + unsigned long flags;
> +
> + if (!device_valid(dev)) {
> + DBG("Invalid device\n");
> + return;
> + }
> +
> + spin_lock_irqsave(&dev->qmi.clients_lock, flags);
> + ret = client_delread(dev, cid, 0, &rbuf, &rbufsize);
> + spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
> +
> + if (!ret) {
> + DBG("WDS callback failed to get data\n");
> + return;
> + }
> +
> + dstats.linkstate = !qc_isdown(dev, DOWN_NO_NDIS_CONNECTION);
> + dstats.reconfigure = false;
> +
> + result = qmiwds_event_resp(rbuf, rbufsize, &dstats);
> + if (result < 0) {
> + DBG("bad WDS packet\n");
> + } else {
> + if (dstats.txofl != (u32)-1)
> + stats->tx_fifo_errors = dstats.txofl;
> +
> + if (dstats.rxofl != (u32)-1)
> + stats->rx_fifo_errors = dstats.rxofl;
> +
> + if (dstats.txerr != (u32)-1)
> + stats->tx_errors = dstats.txerr;
> +
> + if (dstats.rxerr != (u32)-1)
> + stats->rx_errors = dstats.rxerr;
> +
> + if (dstats.txok != (u32)-1)
> + stats->tx_packets = dstats.txok + stats->tx_errors;
> +
> + if (dstats.rxok != (u32)-1)
> + stats->rx_packets = dstats.rxok + stats->rx_errors;
> +
> + if (dstats.txbytesok != (u64)-1)
> + stats->tx_bytes = dstats.txbytesok;
> +
> + if (dstats.rxbytesok != (u64)-1)
> + stats->rx_bytes = dstats.rxbytesok;
> +
> + if (dstats.reconfigure) {
> + DBG("Net device link reset\n");
> + qc_setdown(dev, DOWN_NO_NDIS_CONNECTION);
> + qc_cleardown(dev, DOWN_NO_NDIS_CONNECTION);
> + } else {
> + if (dstats.linkstate) {
> + DBG("Net device link is connected\n");
> + qc_cleardown(dev, DOWN_NO_NDIS_CONNECTION);
> + } else {
> + DBG("Net device link is disconnected\n");
> + qc_setdown(dev, DOWN_NO_NDIS_CONNECTION);
> + }
> + }
> + }
> +
> + kfree(rbuf);
> +
> + result = read_async(dev, cid, 0, wds_callback, data);
> + if (result != 0)
> + DBG("unable to setup next async read\n");
> +}
> +
> +static int setup_wds_callback(struct qcusbnet * dev)
> +{
> + int result;
> + void *buf;
> + size_t size;
> + u16 cid;
> +
> + if (!device_valid(dev)) {
> + DBG("Invalid device\n");
> + return -EFAULT;
> + }
> +
> + result = client_alloc(dev, QMIWDS);
> + if (result < 0)
> + return result;
> + cid = result;
> +
> + buf = qmiwds_new_seteventreport(1, &size);
> + if (!buf)
> + return -ENOMEM;
> +
> + result = write_sync(dev, buf, size, cid);
> + kfree(buf);
> +
> + if (result < 0) {
> + return result;
> + }
> +
> + buf = qmiwds_new_getpkgsrvcstatus(2, &size);
> + if (buf == NULL)
> + return -ENOMEM;
> +
> + result = write_sync(dev, buf, size, cid);
> + kfree(buf);
> +
> + if (result < 0)
> + return result;
> +
> + result = read_async(dev, cid, 0, wds_callback, NULL);
> + if (result) {
> + DBG("unable to setup async read\n");
> + return result;
> + }
> +
> + result = usb_control_msg(dev->usbnet->udev,
> + usb_sndctrlpipe(dev->usbnet->udev, 0),
> + 0x22, 0x21, 1, 0, NULL, 0, 100);
> + if (result < 0) {
> + DBG("Bad SetControlLineState status %d\n", result);
> + return result;
> + }
> +
> + return 0;
> +}
> +
> +static int qmidms_getmeid(struct qcusbnet * dev)
> +{
> + int result;
> + void * wbuf;
> + size_t wbufsize;
> + void * rbuf;
> + u16 rbufsize;
> + u16 cid;
> +
> + if (!device_valid(dev)) {
> + DBG("Invalid device\n");
> + return -EFAULT;
> + }
> +
> + result = client_alloc(dev, QMIDMS);
> + if (result < 0)
> + return result;
> + cid = result;
> +
> + wbuf = qmidms_new_getmeid(1, &wbufsize);
> + if (!wbuf)
> + return -ENOMEM;
> +
> + result = write_sync(dev, wbuf, wbufsize, cid);
> + kfree(wbuf);
> +
> + if (result < 0)
> + return result;
> +
> + result = read_sync(dev, &rbuf, cid, 1);
> + if (result < 0)
> + return result;
> + rbufsize = result;
> +
> + result = qmidms_meid_resp(rbuf, rbufsize, &dev->meid[0], 14);
> + kfree(rbuf);
> +
> + if (result < 0) {
> + DBG("bad get MEID resp\n");
> + memset(&dev->meid[0], '0', 14);
> + }
> +
> + client_free(dev, cid);
> + return 0;
> +}
> +
> +module_param(qcusbnet2k_fwdelay, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(qcusbnet2k_fwdelay, "Delay for old firmware");
> diff --git a/drivers/net/usb/qcusbnet/qmidevice.h b/drivers/net/usb/qcusbnet/qmidevice.h
> new file mode 100755
> index 0000000..39a4b7f
> --- /dev/null
> +++ b/drivers/net/usb/qcusbnet/qmidevice.h
> @@ -0,0 +1,36 @@
> +/* qmidevice.h - gobi QMI device header
> + * Copyright (c) 2010, Code Aurora Forum. 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
> + * only version 2 as published by the Free Software Foundation.
> +
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> +
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> + * 02110-1301, USA.
> + */
> +
> +#ifndef QCUSBNET_QMIDEVICE_H
> +#define QCUSBNET_QMIDEVICE_H
> +
> +#include "structs.h"
> +#include "qmi.h"
> +
> +void printhex(const void *data, size_t size);
> +void qc_setdown(struct qcusbnet *dev, u8 reason);
> +void qc_cleardown(struct qcusbnet *dev, u8 reason);
> +bool qc_isdown(struct qcusbnet *dev, u8 reason);
> +
> +int qc_startread(struct qcusbnet *dev);
> +void qc_stopread(struct qcusbnet *dev);
> +
> +int qc_register(struct qcusbnet *dev);
> +void qc_deregister(struct qcusbnet *dev);
> +
> +#endif /* !QCUSBNET_QMIDEVICE_H */
> diff --git a/drivers/net/usb/qcusbnet/readme.txt b/drivers/net/usb/qcusbnet/readme.txt
> new file mode 100755
> index 0000000..cec6790
> --- /dev/null
> +++ b/drivers/net/usb/qcusbnet/readme.txt
> @@ -0,0 +1,118 @@
> +Gobi2000 network driver for HP 1.0.110
> +06/18/2010
> +
> +This readme covers important information concerning
> +the QCUSBNet2kHP driver, provided in correlation with
> +the Gobi2000-Linux-Package.
> +
> +Table of Contents
> +
> +1. Prerequisites
> +2. Installation instructions
> +3. What's new in this release
> +4. Notes
> +5. Known issues
> +
> +-------------------------------------------------------------------------------
> +
> +1. PREREQUISITES
> +
> +a. Kernel headers or full kernel source installed for the currently
> + running kernel. There must be a link "/lib/modules/<uname -r>/build"
> + that points to these kernel headers or sources.
> +b. The kernel must support the usbcore and usbnet drivers, either as
> + modules or built into the kernel.
> +c. Tools required for building the kernel. These tools usually come in a
> + package called "kernel-devel".
> +d. "gcc" compiler
> +e. "make" tool
> +
> +-------------------------------------------------------------------------------
> +
> +2. INSTALLATION INSTRUCTIONS
> +
> +a. Navigate to the folder "QCUSBNet2k" that contains:
> + Makefile
> + QCUSBNetHP.c
> + QMIDevice.c
> + qmidevice.h
> + structs.h
> + QMI.c
> + qmi.h
> +b. (only required for kernels prior to 2.6.25) Create a symbolic link
> + to the usbnet.h file in your kernel sources:
> + ln -s <linux sources>/drivers/net/usb/usbnet.h ./
> +c. Run the command:
> + make
> +d. Copy the newly created QCUSBNet2kHP.ko into the directory
> + /lib/modules/`uname -r`/kernel/drivers/net/usb/
> +e. Run the command:
> + depmod
> +f. (Optional) Load the driver manually with the command:
> + modprobe QCUSBNet2kHP
> + - This is only required the first time after you install the
> + drivers. If you restart or plug the Gobi device in the drivers
> + will be automatically loaded.
> +
> +-------------------------------------------------------------------------------
> +
> +3. WHAT'S NEW
> +
> +This Release (Gobi2000 network driver for HP 1.0.110) 06/18/2010
> +a. Correctly initialize semaphore during probe function.
> +
> +Prior Release (Gobi2000 network driver for HP 1.0.100) 06/02/2010
> +a. Merge QCQMI driver into QCUSBNet2k driver, removing dependency.
> +
> +Prior Release (Gobi2000 network driver for HP 1.0.90) 05/13/2010
> +a. Fix devqmi_close() from a thread
> +b. Add 2.6.33 kernel support
> +
> +Prior Release (Gobi2000 network driver for HP 1.0.80) 04/19/2010
> +a. Add support to check for duplicate or out of sequence QMI messages.
> +
> +Prior Release (Gobi2000 network driver for HP 1.0.70) 03/15/2010
> +a. Added support for CDC CONNECTION_SPEED_CHANGE indication.
> +b. Modified device cleanup function to better handle the device
> + losing power during suspend or hibernate.
> +c. Replaced autosuspend feature with more aggressive "Selective suspend"
> + style power management. Even if QCWWAN2k or Network connections are
> + active the device will still enter autosuspend after the delay time,
> + and wake on remote wakeup or new data being sent.
> +
> +Prior Release (Gobi2000 Serial driver for HP 1.0.60) 02/16/2010
> +a. Fix to allow proper fork() functionality
> +b. Add supports_autosuspend flag
> +c. Ignore EOVERFLOW errors on interrupt endpoint and resubmit URB
> +d. Change driver versions to match installer package
> +e. Minor update for 2.6.23 kernel compatibility
> +
> +Prior Release (in correlation with Gobi2000-Linux-Package 1.0.30) 12/04/2009
> +a. Modify ioctl numbering to avoid conflict in 2.6.31 kernel
> + This release is only compatible with GOBI2000_LINUX_SDK 1.0.30 and newer.
> +b. Made minor compatibility changes for 2.6.31 kernel
> +
> +Prior Release (in correlation with Gobi2000-Linux-Package 1.0.20) 11/20/2009
> +a. Initial release
> +
> +-------------------------------------------------------------------------------
> +
> +4. NOTES
> +
> +a. In Composite mode, the Gobi device will enumerate a network interface
> + usb<#> where <#> signifies the next available USB network interface.
> +b. In Composite mode, the Gobi device will enumerate a device node
> + /dev/qcqmi<#>. This device node is for use by the QCWWANCMAPI2k.
> +c. Ownership, group, and permissions are managed by your system
> + configuration.
> +
> +-------------------------------------------------------------------------------
> +
> +5. KNOWN ISSUES
> +
> +No known issues.
> +
> +-------------------------------------------------------------------------------
> +
> +
> +
> diff --git a/drivers/net/usb/qcusbnet/structs.h b/drivers/net/usb/qcusbnet/structs.h
> new file mode 100755
> index 0000000..0f9f4eb
> --- /dev/null
> +++ b/drivers/net/usb/qcusbnet/structs.h
> @@ -0,0 +1,99 @@
> +/* structs.h - shared structures
> + * Copyright (c) 2010, Code Aurora Forum. 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
> + * only version 2 as published by the Free Software Foundation.
> +
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> +
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> + * 02110-1301, USA.
> + */
> +
> +#ifndef QCUSBNET_STRUCTS_H
> +#define QCUSBNET_STRUCTS_H
> +
> +#include <linux/etherdevice.h>
> +#include <linux/ethtool.h>
> +#include <linux/mii.h>
> +#include <linux/usb.h>
> +#include <linux/version.h>
> +#include <linux/cdev.h>
> +#include <linux/kthread.h>
> +
> +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,24)
> + #include "usbnet.h"
> +#else
> + #include <linux/usb/usbnet.h>
> +#endif
> +
> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)
> + #include <linux/fdtable.h>
> +#else
> + #include <linux/file.h>
> +#endif
> +
> +#define DBG(format, arg...) \
> + if (debug == 1) { \
> + printk(KERN_INFO "QCUSBNet2k::%s " format, __FUNCTION__, ## arg); \
> + }
> +
> +struct qcusbnet;
> +
> +struct urbreq {
> + struct list_head node;
> + struct urb *urb;
> +};
> +
> +#define DEFAULT_READ_URB_LENGTH 0x1000
> +
> +struct worker {
> + struct task_struct *thread;
> + struct completion work;
> + struct list_head urbs;
> + spinlock_t urbs_lock;
> + struct urb *active;
> + spinlock_t active_lock;
> + struct usb_interface *iface;
> +};
> +
> +struct qmidev {
> + dev_t devnum;
> + struct class *devclass;
> + struct cdev cdev;
> + struct urb *readurb;
> + struct urbsetup *readsetup;
> + void *readbuf;
> + struct urb *inturb;
> + void *intbuf;
> + struct list_head clients;
> + spinlock_t clients_lock;
> + atomic_t qmitid;
> +};
> +
> +enum {
> + DOWN_NO_NDIS_CONNECTION = 0,
> + DOWN_CDC_CONNECTION_SPEED = 1,
> + DOWN_DRIVER_SUSPENDED = 2,
> + DOWN_NET_IFACE_STOPPED = 3,
> +};
> +
> +struct qcusbnet {
> + struct usbnet *usbnet;
> + struct usb_interface *iface;
> + int (*open)(struct net_device *);
> + int (*stop)(struct net_device *);
> + unsigned long down;
> + bool valid;
> + struct qmidev qmi;
> + char meid[14];
> + struct worker worker;
> +};
> +
> +#endif /* !QCUSBNET_STRUCTS_H */
^ permalink raw reply
* Re: [PATCH] net: Implement Any-IP support for IPv6.
From: David Miller @ 2010-09-28 21:17 UTC (permalink / raw)
To: zenczykowski; +Cc: netdev
In-Reply-To: <AANLkTim05yqEiN+LbL5d3Eear0WMcG5LtCi5Ba_StvYv@mail.gmail.com>
From: Maciej Żenczykowski <zenczykowski@gmail.com>
Date: Tue, 28 Sep 2010 14:04:47 -0700
> AFAIK, Tom Herbert did post the ipv4 patch some time back.
>
> http://patchwork.ozlabs.org/patch/53381/
>
> I think the ipv6 code path (and hence the patch itself) is much cleaner.
Ok, I've moved that patch back to under-review state, maybe we
can get them both in at the same time.
Thanks.
^ permalink raw reply
* Re: [PATCH] net: Implement Any-IP support for IPv6.
From: Maciej Żenczykowski @ 2010-09-28 21:04 UTC (permalink / raw)
To: David Miller; +Cc: netdev
In-Reply-To: <20100928.135923.59699712.davem@davemloft.net>
AFAIK, Tom Herbert did post the ipv4 patch some time back.
http://patchwork.ozlabs.org/patch/53381/
I think the ipv6 code path (and hence the patch itself) is much cleaner.
^ permalink raw reply
* Re: [PATCH] net: Implement Any-IP support for IPv6.
From: David Miller @ 2010-09-28 20:59 UTC (permalink / raw)
To: zenczykowski; +Cc: netdev, maze
In-Reply-To: <1285582022-30787-1-git-send-email-zenczykowski@gmail.com>
From: Maciej Żenczykowski <zenczykowski@gmail.com>
Date: Mon, 27 Sep 2010 03:07:02 -0700
> From: Maciej Żenczykowski <maze@google.com>
>
> AnyIP is the capability to receive packets and establish incoming
> connections on IPs we have not explicitly configured on the machine.
>
> An example use case is to configure a machine to accept all incoming
> traffic on eth0, and leave the policy of whether traffic for a given IP
> should be delivered to the machine up to the load balancer.
>
> Can be setup as follows:
> ip -6 rule from all iif eth0 lookup 200
> ip -6 route add local default dev lo table 200
> (in this case for all IPv6 addresses)
>
> Signed-off-by: Maciej Żenczykowski <maze@google.com>
Does this already work on the ipv4 side?
If not, why only add it to ipv6?
^ permalink raw reply
* Re: [PATCH] net: Fix IPv6 PMTU disc. w/ asymmetric routes
From: David Miller @ 2010-09-28 20:58 UTC (permalink / raw)
To: zenczykowski; +Cc: netdev, maze
In-Reply-To: <1285581957-30694-1-git-send-email-zenczykowski@gmail.com>
From: Maciej Żenczykowski <zenczykowski@gmail.com>
Date: Mon, 27 Sep 2010 03:05:57 -0700
> From: Maciej Żenczykowski <maze@google.com>
>
> Signed-off-by: Maciej Żenczykowski <maze@google.com>
Please handle all 4 cases just like the ipv4 routing code does:
{ saddr = SADDR, ifindex = dev->ifindex }
{ saddr = SADDR, ifindex = 0 }
{ saddr = INADDR_ANY, ifindex = dev->ifindex }
{ saddr = INADDR_ANY, ifindex = 0 }
I believe I've specifically asked for this every time someone
mentioned that they wanted to fix this issue.
And the ipv4 side is a good guide in other ways, it uses a set
of two arrays so you can just loop over them, making your
rt6_do_pmtu_disc() calls along the way.
^ permalink raw reply
* Re: [PATCH 0/4] cxgbi: bug fixes and driver update
From: Mike Christie @ 2010-09-28 20:45 UTC (permalink / raw)
To: kxie; +Cc: netdev, linux-scsi, open-iscsi, rranjan, James.Bottomley, davem
In-Reply-To: <201009232343.o8NNhN0E006459@localhost.localdomain>
On 09/23/2010 06:43 PM, kxie@chelsio.com wrote:
> [PATCH 0/4] cxgbi: bug fixes and driver update
>
> From: Karen Xie<kxie@chelsio.com>
>
> This patchset includes:
> - renamed alloc_cpl() to alloc_wr(),
> - fixed locking in pdu read,
> - fixed cxgb3i connecting over VLAN, and
> - updated cxgb4i connection setting and ddp programming.
>
Looks ok.
Reviewed-by: Mike Christie <michaelc@cs.wisc.edu>
^ permalink raw reply
* Re: [PATCHv2 net-next-2.6 0/5] XFRM,IPv6: Removal of RH2/HAO from IPsec-protected MIPv6 traffic
From: David Miller @ 2010-09-28 20:38 UTC (permalink / raw)
To: arno; +Cc: eric.dumazet, herbert, yoshfuji, netdev
In-Reply-To: <cover.1285687738.git.arno@natisbad.org>
From: Arnaud Ebalard <arno@natisbad.org>
Date: Tue, 28 Sep 2010 17:53:50 +0200
> This an updated version of the patches. For reference, introduction of
> the feature is here http://thread.gmane.org/gmane.linux.network/172941
> Just tell me if you prefer I resend the full intro each time.
>
> Compared to initial version, this v2 is
>
> - against net-next-2.6
> - fixed and more exhaustively build tested for various kernel
> configuration (w/ and w/o IPv6, IPv4, MIPv6, XFRM*, RO, ...).
> It adds some #ifdef but I did not found good ways to spare those.
> - built and tested on both current linux-2.6 and net-next-2.6 (*)
>
> Comments welcome.
Try again, this time with ipv6 modular:
net/built-in.o: In function `xfrm_input_addr_check':
/home/davem/src/GIT/net-next-2.6/net/xfrm/xfrm_input.c:115: undefined reference to `xfrm6_input_addr_check'
You can't put xfrm6_input_addr_check into the ipv6.o object build if you want to
call it from the generic xfrm stack which is always built statically.
Put this and xfrm4_input_addr_check where it belongs, as an afinfo->op()
^ permalink raw reply
* Re: [PATCH] net-2.6: SYN retransmits: Add new parameter to retransmits_timed_out()
From: David Miller @ 2010-09-28 20:08 UTC (permalink / raw)
To: damian; +Cc: netdev
In-Reply-To: <1285703094.7187.3.camel@nexus>
From: Damian Lukowski <damian@tvk.rwth-aachen.de>
Date: Tue, 28 Sep 2010 21:44:54 +0200
>
> This patch adds a syn_set parameter to the retransmits_timed_out()
> routine and updates its callers. If not set, TCP_RTO_MIN is taken
> as the calculation basis as before. If set, TCP_TIMEOUT_INIT is
> used instead, so that sysctl_syn_retries represents the actual
> amount of SYN retransmissions in case no SYNACKs are received when
> establishing a new connection.
>
> Signed-off-by: Damian Lukowski <damian@tvk.rwth-aachen.de>
Applied, thanks Damian.
^ permalink raw reply
* [PATCH 2/2] net-next-2.6: SYN retransmits: Add new parameter to retransmits_timed_out()
From: Damian Lukowski @ 2010-09-28 19:46 UTC (permalink / raw)
To: netdev
This patch adds a syn_set parameter to the retransmits_timed_out()
routine and updates its callers. If not set, TCP_RTO_MIN is taken
as the calculation basis as before. If set, TCP_TIMEOUT_INIT is
used instead, so that sysctl_syn_retries represents the actual
amount of SYN retransmissions in case no SYNACKs are received when
establishing a new connection.
Signed-off-by: Damian Lukowski <damian@tvk.rwth-aachen.de>
---
net/ipv4/tcp_timer.c | 26 +++++++++++++++-----------
1 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 98cbc60..56a5aaf 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -135,13 +135,16 @@ static void tcp_mtu_probing(struct inet_connection_sock *icsk, struct sock *sk)
/* This function calculates a "timeout" which is equivalent to the timeout of a
* TCP connection after "boundary" unsuccessful, exponentially backed-off
- * retransmissions with an initial RTO of TCP_RTO_MIN.
+ * retransmissions with an initial RTO of TCP_RTO_MIN or TCP_TIMEOUT_INIT if
+ * syn_set flag is set.
*/
static bool retransmits_timed_out(struct sock *sk,
unsigned int boundary,
- unsigned int timeout)
+ unsigned int timeout,
+ bool syn_set)
{
unsigned int backoff_thresh, start_ts;
+ unsigned int rto_base = syn_set ? TCP_TIMEOUT_INIT : TCP_RTO_MIN;
if (!inet_csk(sk)->icsk_retransmits)
return false;
@@ -152,12 +155,12 @@ static bool retransmits_timed_out(struct sock *sk,
start_ts = tcp_sk(sk)->retrans_stamp;
if (likely(timeout == 0)) {
- backoff_thresh = ilog2(TCP_RTO_MAX/TCP_RTO_MIN);
+ backoff_thresh = ilog2(TCP_RTO_MAX/rto_base);
if (boundary <= backoff_thresh)
- timeout = ((2 << boundary) - 1) * TCP_RTO_MIN;
+ timeout = ((2 << boundary) - 1) * rto_base;
else
- timeout = ((2 << backoff_thresh) - 1) * TCP_RTO_MIN +
+ timeout = ((2 << backoff_thresh) - 1) * rto_base +
(boundary - backoff_thresh) * TCP_RTO_MAX;
}
return (tcp_time_stamp - start_ts) >= timeout;
@@ -168,14 +171,15 @@ static int tcp_write_timeout(struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
int retry_until;
- bool do_reset;
+ bool do_reset, syn_set = 0;
if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
if (icsk->icsk_retransmits)
dst_negative_advice(sk);
retry_until = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries;
+ syn_set = 1;
} else {
- if (retransmits_timed_out(sk, sysctl_tcp_retries1, 0)) {
+ if (retransmits_timed_out(sk, sysctl_tcp_retries1, 0, 0)) {
/* Black hole detection */
tcp_mtu_probing(icsk, sk);
@@ -188,7 +192,8 @@ static int tcp_write_timeout(struct sock *sk)
retry_until = tcp_orphan_retries(sk, alive);
do_reset = alive ||
- !retransmits_timed_out(sk, retry_until, 0);
+ !retransmits_timed_out(sk,
+ retry_until, 0, 0);
if (tcp_out_of_resources(sk, do_reset))
return 1;
@@ -196,8 +201,7 @@ static int tcp_write_timeout(struct sock *sk)
}
if (retransmits_timed_out(sk, retry_until,
- (1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV) ? 0 :
- icsk->icsk_user_timeout)) {
+ syn_set ? 0 : icsk->icsk_user_timeout, syn_set)) {
/* Has it gone just too far? */
tcp_write_err(sk);
return 1;
@@ -439,7 +443,7 @@ out_reset_timer:
icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
}
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);
- if (retransmits_timed_out(sk, sysctl_tcp_retries1 + 1, 0))
+ if (retransmits_timed_out(sk, sysctl_tcp_retries1 + 1, 0, 0))
__sk_dst_reset(sk);
out:;
--
1.7.2.2
^ permalink raw reply related
* [PATCH 1/2] net-next-2.6: SYN retransmits: Rename threshold variable
From: Damian Lukowski @ 2010-09-28 19:45 UTC (permalink / raw)
To: netdev
This preparatory patch renames the threshold variable in
retransmits_timed_out() for proper code style.
Signed-off-by: Damian Lukowski <damian@tvk.rwth-aachen.de>
---
net/ipv4/tcp_timer.c | 10 +++++-----
1 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index baea4a1..98cbc60 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -141,7 +141,7 @@ static bool retransmits_timed_out(struct sock *sk,
unsigned int boundary,
unsigned int timeout)
{
- unsigned int linear_backoff_thresh, start_ts;
+ unsigned int backoff_thresh, start_ts;
if (!inet_csk(sk)->icsk_retransmits)
return false;
@@ -152,13 +152,13 @@ static bool retransmits_timed_out(struct sock *sk,
start_ts = tcp_sk(sk)->retrans_stamp;
if (likely(timeout == 0)) {
- linear_backoff_thresh = ilog2(TCP_RTO_MAX/TCP_RTO_MIN);
+ backoff_thresh = ilog2(TCP_RTO_MAX/TCP_RTO_MIN);
- if (boundary <= linear_backoff_thresh)
+ if (boundary <= backoff_thresh)
timeout = ((2 << boundary) - 1) * TCP_RTO_MIN;
else
- timeout = ((2 << linear_backoff_thresh) - 1) * TCP_RTO_MIN +
- (boundary - linear_backoff_thresh) * TCP_RTO_MAX;
+ timeout = ((2 << backoff_thresh) - 1) * TCP_RTO_MIN +
+ (boundary - backoff_thresh) * TCP_RTO_MAX;
}
return (tcp_time_stamp - start_ts) >= timeout;
}
--
1.7.2.2
^ permalink raw reply related
* [PATCH] net-2.6: SYN retransmits: Add new parameter to retransmits_timed_out()
From: Damian Lukowski @ 2010-09-28 19:44 UTC (permalink / raw)
To: netdev
This patch adds a syn_set parameter to the retransmits_timed_out()
routine and updates its callers. If not set, TCP_RTO_MIN is taken
as the calculation basis as before. If set, TCP_TIMEOUT_INIT is
used instead, so that sysctl_syn_retries represents the actual
amount of SYN retransmissions in case no SYNACKs are received when
establishing a new connection.
Signed-off-by: Damian Lukowski <damian@tvk.rwth-aachen.de>
---
net/ipv4/tcp_timer.c | 24 ++++++++++++++----------
1 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index c35b469..74c54b3 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -135,13 +135,16 @@ static void tcp_mtu_probing(struct inet_connection_sock *icsk, struct sock *sk)
/* This function calculates a "timeout" which is equivalent to the timeout of a
* TCP connection after "boundary" unsuccessful, exponentially backed-off
- * retransmissions with an initial RTO of TCP_RTO_MIN.
+ * retransmissions with an initial RTO of TCP_RTO_MIN or TCP_TIMEOUT_INIT if
+ * syn_set flag is set.
*/
static bool retransmits_timed_out(struct sock *sk,
- unsigned int boundary)
+ unsigned int boundary,
+ bool syn_set)
{
unsigned int timeout, linear_backoff_thresh;
unsigned int start_ts;
+ unsigned int rto_base = syn_set ? TCP_TIMEOUT_INIT : TCP_RTO_MIN;
if (!inet_csk(sk)->icsk_retransmits)
return false;
@@ -151,12 +154,12 @@ static bool retransmits_timed_out(struct sock *sk,
else
start_ts = tcp_sk(sk)->retrans_stamp;
- linear_backoff_thresh = ilog2(TCP_RTO_MAX/TCP_RTO_MIN);
+ linear_backoff_thresh = ilog2(TCP_RTO_MAX/rto_base);
if (boundary <= linear_backoff_thresh)
- timeout = ((2 << boundary) - 1) * TCP_RTO_MIN;
+ timeout = ((2 << boundary) - 1) * rto_base;
else
- timeout = ((2 << linear_backoff_thresh) - 1) * TCP_RTO_MIN +
+ timeout = ((2 << linear_backoff_thresh) - 1) * rto_base +
(boundary - linear_backoff_thresh) * TCP_RTO_MAX;
return (tcp_time_stamp - start_ts) >= timeout;
@@ -167,14 +170,15 @@ static int tcp_write_timeout(struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
int retry_until;
- bool do_reset;
+ bool do_reset, syn_set = 0;
if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
if (icsk->icsk_retransmits)
dst_negative_advice(sk);
retry_until = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries;
+ syn_set = 1;
} else {
- if (retransmits_timed_out(sk, sysctl_tcp_retries1)) {
+ if (retransmits_timed_out(sk, sysctl_tcp_retries1, 0)) {
/* Black hole detection */
tcp_mtu_probing(icsk, sk);
@@ -187,14 +191,14 @@ static int tcp_write_timeout(struct sock *sk)
retry_until = tcp_orphan_retries(sk, alive);
do_reset = alive ||
- !retransmits_timed_out(sk, retry_until);
+ !retransmits_timed_out(sk, retry_until, 0);
if (tcp_out_of_resources(sk, do_reset))
return 1;
}
}
- if (retransmits_timed_out(sk, retry_until)) {
+ if (retransmits_timed_out(sk, retry_until, syn_set)) {
/* Has it gone just too far? */
tcp_write_err(sk);
return 1;
@@ -436,7 +440,7 @@ out_reset_timer:
icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
}
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);
- if (retransmits_timed_out(sk, sysctl_tcp_retries1 + 1))
+ if (retransmits_timed_out(sk, sysctl_tcp_retries1 + 1, 0))
__sk_dst_reset(sk);
out:;
--
1.7.2.2
^ permalink raw reply related
* Re: [PATCH] de2104x: remove experimental status
From: Jeff Garzik @ 2010-09-28 18:52 UTC (permalink / raw)
To: Ondrej Zary; +Cc: netdev, Kernel development list
In-Reply-To: <201009282046.21279.linux@rainbow-software.org>
On 09/28/2010 02:46 PM, Ondrej Zary wrote:
> It should be ready after 8 years...remove the experimental dependency.
>
> Signed-off-by: Ondrej Zary<linux@rainbow-software.org>
>
> --- linux-2.6.36-rc3-/drivers/net/tulip/Kconfig 2010-08-29 17:36:04.000000000 +0200
> +++ linux-2.6.36-rc3/drivers/net/tulip/Kconfig 2010-09-28 19:49:46.000000000 +0200
> @@ -11,8 +11,8 @@ menuconfig NET_TULIP
> if NET_TULIP
>
> config DE2104X
> - tristate "Early DECchip Tulip (dc2104x) PCI support (EXPERIMENTAL)"
> - depends on PCI&& EXPERIMENTAL
> + tristate "Early DECchip Tulip (dc2104x) PCI support"
> + depends on PCI
> select CRC32
> ---help---
> This driver is developed for the SMC EtherPower series Ethernet
Well... it's not the years, it's the quality... which I think has been
sufficiently increased.
Acked-by: Jeff Garzik <jgarzik@redhat.com>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox