aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorPetko Manolov <petkan@nucleusys.com>2013-04-25 18:41:50 -0400
committerDavid S. Miller <davem@davemloft.net>2013-04-29 13:57:50 -0400
commit323b34963d113efb566635f43858f40cce01d5f9 (patch)
treecce217081da276ac54ad37b3ffa78c2da9e54ae8 /drivers/net
parent2bd647018fe1b20c5c11dd1c508baea5771cd074 (diff)
drivers: net: usb: pegasus: fix control urb submission
Pegasus driver used single callback for sync and async control URBs. Special flags were employed to distinguish between both, but due to flawed logic it didn't always work. As a result of this change [get|set]_registers() are now much simpler. Async write is also leaner and does not use single, statically allocated memory for usb_ctrlrequest, which is another potential race when asynchronously submitting URBs. Signed-off-by: Petko Manolov <petkan@nucleusys.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/usb/pegasus.c285
-rw-r--r--drivers/net/usb/pegasus.h8
2 files changed, 76 insertions, 217 deletions
diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c
index 352b4dcee61c..09699054b54f 100644
--- a/drivers/net/usb/pegasus.c
+++ b/drivers/net/usb/pegasus.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (c) 1999-2005 Petko Manolov (petkan@users.sourceforge.net) 2 * Copyright (c) 1999-2013 Petko Manolov (petkan@nucleusys.com)
3 * 3 *
4 * This program is free software; you can redistribute it and/or modify 4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as 5 * it under the terms of the GNU General Public License version 2 as
@@ -26,6 +26,9 @@
26 * v0.5.1 ethtool support added 26 * v0.5.1 ethtool support added
27 * v0.5.5 rx socket buffers are in a pool and the their allocation 27 * v0.5.5 rx socket buffers are in a pool and the their allocation
28 * is out of the interrupt routine. 28 * is out of the interrupt routine.
29 * ...
30 * v0.9.3 simplified [get|set]_register(s), async update registers
31 * logic revisited, receive skb_pool removed.
29 */ 32 */
30 33
31#include <linux/sched.h> 34#include <linux/sched.h>
@@ -45,8 +48,8 @@
45/* 48/*
46 * Version Information 49 * Version Information
47 */ 50 */
48#define DRIVER_VERSION "v0.6.14 (2006/09/27)" 51#define DRIVER_VERSION "v0.9.3 (2013/04/25)"
49#define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>" 52#define DRIVER_AUTHOR "Petko Manolov <petkan@nucleusys.com>"
50#define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" 53#define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver"
51 54
52static const char driver_name[] = "pegasus"; 55static const char driver_name[] = "pegasus";
@@ -108,218 +111,90 @@ MODULE_PARM_DESC(msg_level, "Override default message level");
108MODULE_DEVICE_TABLE(usb, pegasus_ids); 111MODULE_DEVICE_TABLE(usb, pegasus_ids);
109static const struct net_device_ops pegasus_netdev_ops; 112static const struct net_device_ops pegasus_netdev_ops;
110 113
111static int update_eth_regs_async(pegasus_t *); 114/*****/
112/* Aargh!!! I _really_ hate such tweaks */ 115
113static void ctrl_callback(struct urb *urb) 116static void async_ctrl_callback(struct urb *urb)
114{ 117{
115 pegasus_t *pegasus = urb->context; 118 struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
116 int status = urb->status; 119 int status = urb->status;
117 120
118 if (!pegasus) 121 if (status < 0)
119 return; 122 dev_dbg(&urb->dev->dev, "%s failed with %d", __func__, status);
120 123 kfree(req);
121 switch (status) { 124 usb_free_urb(urb);
122 case 0:
123 if (pegasus->flags & ETH_REGS_CHANGE) {
124 pegasus->flags &= ~ETH_REGS_CHANGE;
125 pegasus->flags |= ETH_REGS_CHANGED;
126 update_eth_regs_async(pegasus);
127 return;
128 }
129 break;
130 case -EINPROGRESS:
131 return;
132 case -ENOENT:
133 break;
134 default:
135 if (net_ratelimit())
136 netif_dbg(pegasus, drv, pegasus->net,
137 "%s, status %d\n", __func__, status);
138 break;
139 }
140 pegasus->flags &= ~ETH_REGS_CHANGED;
141 wake_up(&pegasus->ctrl_wait);
142} 125}
143 126
144static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, 127static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
145 void *data)
146{ 128{
147 int ret; 129 int ret;
148 char *buffer;
149 DECLARE_WAITQUEUE(wait, current);
150
151 buffer = kmalloc(size, GFP_KERNEL);
152 if (!buffer)
153 return -ENOMEM;
154
155 add_wait_queue(&pegasus->ctrl_wait, &wait);
156 set_current_state(TASK_UNINTERRUPTIBLE);
157 while (pegasus->flags & ETH_REGS_CHANGED)
158 schedule();
159 remove_wait_queue(&pegasus->ctrl_wait, &wait);
160 set_current_state(TASK_RUNNING);
161
162 pegasus->dr.bRequestType = PEGASUS_REQT_READ;
163 pegasus->dr.bRequest = PEGASUS_REQ_GET_REGS;
164 pegasus->dr.wValue = cpu_to_le16(0);
165 pegasus->dr.wIndex = cpu_to_le16(indx);
166 pegasus->dr.wLength = cpu_to_le16(size);
167 pegasus->ctrl_urb->transfer_buffer_length = size;
168
169 usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb,
170 usb_rcvctrlpipe(pegasus->usb, 0),
171 (char *) &pegasus->dr,
172 buffer, size, ctrl_callback, pegasus);
173
174 add_wait_queue(&pegasus->ctrl_wait, &wait);
175 set_current_state(TASK_UNINTERRUPTIBLE);
176
177 /* using ATOMIC, we'd never wake up if we slept */
178 if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
179 set_current_state(TASK_RUNNING);
180 if (ret == -ENODEV)
181 netif_device_detach(pegasus->net);
182 if (net_ratelimit())
183 netif_err(pegasus, drv, pegasus->net,
184 "%s, status %d\n", __func__, ret);
185 goto out;
186 }
187
188 schedule();
189out:
190 remove_wait_queue(&pegasus->ctrl_wait, &wait);
191 memcpy(data, buffer, size);
192 kfree(buffer);
193 130
131 ret = usb_control_msg(pegasus->usb, usb_rcvctrlpipe(pegasus->usb, 0),
132 PEGASUS_REQ_GET_REGS, PEGASUS_REQT_READ, 0,
133 indx, data, size, 1000);
134 if (ret < 0)
135 netif_dbg(pegasus, drv, pegasus->net,
136 "%s returned %d\n", __func__, ret);
194 return ret; 137 return ret;
195} 138}
196 139
197static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, 140static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
198 void *data)
199{ 141{
200 int ret; 142 int ret;
201 char *buffer;
202 DECLARE_WAITQUEUE(wait, current);
203
204 buffer = kmemdup(data, size, GFP_KERNEL);
205 if (!buffer) {
206 netif_warn(pegasus, drv, pegasus->net,
207 "out of memory in %s\n", __func__);
208 return -ENOMEM;
209 }
210
211 add_wait_queue(&pegasus->ctrl_wait, &wait);
212 set_current_state(TASK_UNINTERRUPTIBLE);
213 while (pegasus->flags & ETH_REGS_CHANGED)
214 schedule();
215 remove_wait_queue(&pegasus->ctrl_wait, &wait);
216 set_current_state(TASK_RUNNING);
217
218 pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
219 pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS;
220 pegasus->dr.wValue = cpu_to_le16(0);
221 pegasus->dr.wIndex = cpu_to_le16(indx);
222 pegasus->dr.wLength = cpu_to_le16(size);
223 pegasus->ctrl_urb->transfer_buffer_length = size;
224
225 usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb,
226 usb_sndctrlpipe(pegasus->usb, 0),
227 (char *) &pegasus->dr,
228 buffer, size, ctrl_callback, pegasus);
229
230 add_wait_queue(&pegasus->ctrl_wait, &wait);
231 set_current_state(TASK_UNINTERRUPTIBLE);
232
233 if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
234 if (ret == -ENODEV)
235 netif_device_detach(pegasus->net);
236 netif_err(pegasus, drv, pegasus->net,
237 "%s, status %d\n", __func__, ret);
238 goto out;
239 }
240
241 schedule();
242out:
243 remove_wait_queue(&pegasus->ctrl_wait, &wait);
244 kfree(buffer);
245 143
144 ret = usb_control_msg(pegasus->usb, usb_sndctrlpipe(pegasus->usb, 0),
145 PEGASUS_REQ_SET_REGS, PEGASUS_REQT_WRITE, 0,
146 indx, data, size, 100);
147 if (ret < 0)
148 netif_dbg(pegasus, drv, pegasus->net,
149 "%s returned %d\n", __func__, ret);
246 return ret; 150 return ret;
247} 151}
248 152
249static int set_register(pegasus_t *pegasus, __u16 indx, __u8 data) 153static int set_register(pegasus_t *pegasus, __u16 indx, __u8 data)
250{ 154{
251 int ret; 155 int ret;
252 char *tmp;
253 DECLARE_WAITQUEUE(wait, current);
254
255 tmp = kmemdup(&data, 1, GFP_KERNEL);
256 if (!tmp) {
257 netif_warn(pegasus, drv, pegasus->net,
258 "out of memory in %s\n", __func__);
259 return -ENOMEM;
260 }
261 add_wait_queue(&pegasus->ctrl_wait, &wait);
262 set_current_state(TASK_UNINTERRUPTIBLE);
263 while (pegasus->flags & ETH_REGS_CHANGED)
264 schedule();
265 remove_wait_queue(&pegasus->ctrl_wait, &wait);
266 set_current_state(TASK_RUNNING);
267
268 pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
269 pegasus->dr.bRequest = PEGASUS_REQ_SET_REG;
270 pegasus->dr.wValue = cpu_to_le16(data);
271 pegasus->dr.wIndex = cpu_to_le16(indx);
272 pegasus->dr.wLength = cpu_to_le16(1);
273 pegasus->ctrl_urb->transfer_buffer_length = 1;
274
275 usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb,
276 usb_sndctrlpipe(pegasus->usb, 0),
277 (char *) &pegasus->dr,
278 tmp, 1, ctrl_callback, pegasus);
279
280 add_wait_queue(&pegasus->ctrl_wait, &wait);
281 set_current_state(TASK_UNINTERRUPTIBLE);
282
283 if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
284 if (ret == -ENODEV)
285 netif_device_detach(pegasus->net);
286 if (net_ratelimit())
287 netif_err(pegasus, drv, pegasus->net,
288 "%s, status %d\n", __func__, ret);
289 goto out;
290 }
291
292 schedule();
293out:
294 remove_wait_queue(&pegasus->ctrl_wait, &wait);
295 kfree(tmp);
296 156
157 ret = usb_control_msg(pegasus->usb, usb_sndctrlpipe(pegasus->usb, 0),
158 PEGASUS_REQ_SET_REG, PEGASUS_REQT_WRITE, data,
159 indx, &data, 1, 1000);
160 if (ret < 0)
161 netif_dbg(pegasus, drv, pegasus->net,
162 "%s returned %d\n", __func__, ret);
297 return ret; 163 return ret;
298} 164}
299 165
300static int update_eth_regs_async(pegasus_t *pegasus) 166static int update_eth_regs_async(pegasus_t *pegasus)
301{ 167{
302 int ret; 168 int ret = -ENOMEM;
303 169 struct urb *async_urb;
304 pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; 170 struct usb_ctrlrequest *req;
305 pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS;
306 pegasus->dr.wValue = cpu_to_le16(0);
307 pegasus->dr.wIndex = cpu_to_le16(EthCtrl0);
308 pegasus->dr.wLength = cpu_to_le16(3);
309 pegasus->ctrl_urb->transfer_buffer_length = 3;
310 171
311 usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb, 172 req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
312 usb_sndctrlpipe(pegasus->usb, 0), 173 if (req == NULL)
313 (char *) &pegasus->dr, 174 return ret;
314 pegasus->eth_regs, 3, ctrl_callback, pegasus);
315 175
316 if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) { 176 async_urb = usb_alloc_urb(0, GFP_ATOMIC);
177 if (async_urb == NULL) {
178 kfree(req);
179 return ret;
180 }
181 req->bRequestType = PEGASUS_REQT_WRITE;
182 req->bRequest = PEGASUS_REQ_SET_REGS;
183 req->wValue = cpu_to_le16(0);
184 req->wIndex = cpu_to_le16(EthCtrl0);
185 req->wLength = cpu_to_le16(3);
186
187 usb_fill_control_urb(async_urb, pegasus->usb,
188 usb_sndctrlpipe(pegasus->usb, 0), (void *)req,
189 pegasus->eth_regs, 3, async_ctrl_callback, req);
190
191 ret = usb_submit_urb(async_urb, GFP_ATOMIC);
192 if (ret) {
317 if (ret == -ENODEV) 193 if (ret == -ENODEV)
318 netif_device_detach(pegasus->net); 194 netif_device_detach(pegasus->net);
319 netif_err(pegasus, drv, pegasus->net, 195 netif_err(pegasus, drv, pegasus->net,
320 "%s, status %d\n", __func__, ret); 196 "%s returned %d\n", __func__, ret);
321 } 197 }
322
323 return ret; 198 return ret;
324} 199}
325 200
@@ -899,7 +774,6 @@ static void free_all_urbs(pegasus_t *pegasus)
899 usb_free_urb(pegasus->intr_urb); 774 usb_free_urb(pegasus->intr_urb);
900 usb_free_urb(pegasus->tx_urb); 775 usb_free_urb(pegasus->tx_urb);
901 usb_free_urb(pegasus->rx_urb); 776 usb_free_urb(pegasus->rx_urb);
902 usb_free_urb(pegasus->ctrl_urb);
903} 777}
904 778
905static void unlink_all_urbs(pegasus_t *pegasus) 779static void unlink_all_urbs(pegasus_t *pegasus)
@@ -907,47 +781,42 @@ static void unlink_all_urbs(pegasus_t *pegasus)
907 usb_kill_urb(pegasus->intr_urb); 781 usb_kill_urb(pegasus->intr_urb);
908 usb_kill_urb(pegasus->tx_urb); 782 usb_kill_urb(pegasus->tx_urb);
909 usb_kill_urb(pegasus->rx_urb); 783 usb_kill_urb(pegasus->rx_urb);
910 usb_kill_urb(pegasus->ctrl_urb);
911} 784}
912 785
913static int alloc_urbs(pegasus_t *pegasus) 786static int alloc_urbs(pegasus_t *pegasus)
914{ 787{
915 pegasus->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); 788 int res = -ENOMEM;
916 if (!pegasus->ctrl_urb) 789
917 return 0;
918 pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL); 790 pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
919 if (!pegasus->rx_urb) { 791 if (!pegasus->rx_urb) {
920 usb_free_urb(pegasus->ctrl_urb); 792 return res;
921 return 0;
922 } 793 }
923 pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL); 794 pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
924 if (!pegasus->tx_urb) { 795 if (!pegasus->tx_urb) {
925 usb_free_urb(pegasus->rx_urb); 796 usb_free_urb(pegasus->rx_urb);
926 usb_free_urb(pegasus->ctrl_urb); 797 return res;
927 return 0;
928 } 798 }
929 pegasus->intr_urb = usb_alloc_urb(0, GFP_KERNEL); 799 pegasus->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
930 if (!pegasus->intr_urb) { 800 if (!pegasus->intr_urb) {
931 usb_free_urb(pegasus->tx_urb); 801 usb_free_urb(pegasus->tx_urb);
932 usb_free_urb(pegasus->rx_urb); 802 usb_free_urb(pegasus->rx_urb);
933 usb_free_urb(pegasus->ctrl_urb); 803 return res;
934 return 0;
935 } 804 }
936 805
937 return 1; 806 return 0;
938} 807}
939 808
940static int pegasus_open(struct net_device *net) 809static int pegasus_open(struct net_device *net)
941{ 810{
942 pegasus_t *pegasus = netdev_priv(net); 811 pegasus_t *pegasus = netdev_priv(net);
943 int res; 812 int res=-ENOMEM;
944 813
945 if (pegasus->rx_skb == NULL) 814 if (pegasus->rx_skb == NULL)
946 pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net, 815 pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net,
947 PEGASUS_MTU, 816 PEGASUS_MTU,
948 GFP_KERNEL); 817 GFP_KERNEL);
949 if (!pegasus->rx_skb) 818 if (!pegasus->rx_skb)
950 return -ENOMEM; 819 goto exit;
951 820
952 res = set_registers(pegasus, EthID, 6, net->dev_addr); 821 res = set_registers(pegasus, EthID, 6, net->dev_addr);
953 822
@@ -973,7 +842,8 @@ static int pegasus_open(struct net_device *net)
973 usb_kill_urb(pegasus->rx_urb); 842 usb_kill_urb(pegasus->rx_urb);
974 goto exit; 843 goto exit;
975 } 844 }
976 if ((res = enable_net_traffic(net, pegasus->usb))) { 845 res = enable_net_traffic(net, pegasus->usb);
846 if (res < 0) {
977 netif_dbg(pegasus, ifup, net, 847 netif_dbg(pegasus, ifup, net,
978 "can't enable_net_traffic() - %d\n", res); 848 "can't enable_net_traffic() - %d\n", res);
979 res = -EIO; 849 res = -EIO;
@@ -1153,11 +1023,7 @@ static void pegasus_set_multicast(struct net_device *net)
1153 pegasus->eth_regs[EthCtrl0] &= ~RX_MULTICAST; 1023 pegasus->eth_regs[EthCtrl0] &= ~RX_MULTICAST;
1154 pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS; 1024 pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
1155 } 1025 }
1156 1026 update_eth_regs_async(pegasus);
1157 pegasus->ctrl_urb->status = 0;
1158
1159 pegasus->flags |= ETH_REGS_CHANGE;
1160 ctrl_callback(pegasus->ctrl_urb);
1161} 1027}
1162 1028
1163static __u8 mii_phy_probe(pegasus_t *pegasus) 1029static __u8 mii_phy_probe(pegasus_t *pegasus)
@@ -1274,9 +1140,9 @@ static int pegasus_probe(struct usb_interface *intf,
1274 1140
1275 pegasus = netdev_priv(net); 1141 pegasus = netdev_priv(net);
1276 pegasus->dev_index = dev_index; 1142 pegasus->dev_index = dev_index;
1277 init_waitqueue_head(&pegasus->ctrl_wait);
1278 1143
1279 if (!alloc_urbs(pegasus)) { 1144 res = alloc_urbs(pegasus);
1145 if (res < 0) {
1280 dev_err(&intf->dev, "can't allocate %s\n", "urbs"); 1146 dev_err(&intf->dev, "can't allocate %s\n", "urbs");
1281 goto out1; 1147 goto out1;
1282 } 1148 }
@@ -1326,12 +1192,9 @@ static int pegasus_probe(struct usb_interface *intf,
1326 if (res) 1192 if (res)
1327 goto out3; 1193 goto out3;
1328 queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check, 1194 queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check,
1329 CARRIER_CHECK_DELAY); 1195 CARRIER_CHECK_DELAY);
1330 1196 dev_info(&intf->dev, "%s, %s, %pM\n", net->name,
1331 dev_info(&intf->dev, "%s, %s, %pM\n", 1197 usb_dev_id[dev_index].name, net->dev_addr);
1332 net->name,
1333 usb_dev_id[dev_index].name,
1334 net->dev_addr);
1335 return 0; 1198 return 0;
1336 1199
1337out3: 1200out3:
diff --git a/drivers/net/usb/pegasus.h b/drivers/net/usb/pegasus.h
index 00d44e3ff744..d15646244fdf 100644
--- a/drivers/net/usb/pegasus.h
+++ b/drivers/net/usb/pegasus.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (c) 1999-2003 Petko Manolov - Petkan (petkan@users.sourceforge.net) 2 * Copyright (c) 1999-2013 Petko Manolov (petkan@nucleusys.com)
3 * 3 *
4 * This program is free software; you can redistribute it and/or modify 4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as published 5 * it under the terms of the GNU General Public License version 2 as published
@@ -33,8 +33,6 @@
33#define CTRL_URB_SLEEP 0x00000020 33#define CTRL_URB_SLEEP 0x00000020
34#define PEGASUS_UNPLUG 0x00000040 34#define PEGASUS_UNPLUG 0x00000040
35#define PEGASUS_RX_URB_FAIL 0x00000080 35#define PEGASUS_RX_URB_FAIL 0x00000080
36#define ETH_REGS_CHANGE 0x40000000
37#define ETH_REGS_CHANGED 0x80000000
38 36
39#define RX_MULTICAST 2 37#define RX_MULTICAST 2
40#define RX_PROMISCUOUS 4 38#define RX_PROMISCUOUS 4
@@ -95,10 +93,8 @@ typedef struct pegasus {
95 int intr_interval; 93 int intr_interval;
96 struct tasklet_struct rx_tl; 94 struct tasklet_struct rx_tl;
97 struct delayed_work carrier_check; 95 struct delayed_work carrier_check;
98 struct urb *ctrl_urb, *rx_urb, *tx_urb, *intr_urb; 96 struct urb *rx_urb, *tx_urb, *intr_urb;
99 struct sk_buff *rx_skb; 97 struct sk_buff *rx_skb;
100 struct usb_ctrlrequest dr;
101 wait_queue_head_t ctrl_wait;
102 int chip; 98 int chip;
103 unsigned char intr_buff[8]; 99 unsigned char intr_buff[8];
104 __u8 tx_buff[PEGASUS_MTU]; 100 __u8 tx_buff[PEGASUS_MTU];