2 * Copyright (C) 2002 Intersil Americas Inc.
3 * Copyright 2004 Jens Maurer <Jens.Maurer@gmx.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include <linux/netdevice.h>
21 #include <linux/module.h>
22 #include <linux/pci.h>
23 #include <linux/sched.h>
24 #include <linux/slab.h>
27 #include <asm/system.h>
28 #include <linux/if_arp.h>
30 #include "prismcompat.h"
32 #include "islpci_mgt.h"
33 #include "isl_oid.h" /* additional types and defs for isl38xx fw */
34 #include "isl_ioctl.h"
36 #include <net/iw_handler.h>
38 /******************************************************************************
39 Global variable definition section
40 ******************************************************************************/
41 int pc_debug
= VERBOSE
;
42 module_param(pc_debug
, int, 0);
44 /******************************************************************************
45 Driver general functions
46 ******************************************************************************/
47 #if VERBOSE > SHOW_ERROR_MESSAGES
49 display_buffer(char *buffer
, int length
)
51 if ((pc_debug
& SHOW_BUFFER_CONTENTS
) == 0)
55 printk("[%02x]", *buffer
& 255);
64 /*****************************************************************************
65 Queue handling for management frames
66 ******************************************************************************/
69 * Helper function to create a PIMFOR management frame header.
72 pimfor_encode_header(int operation
, u32 oid
, u32 length
, pimfor_header_t
*h
)
74 h
->version
= PIMFOR_VERSION
;
75 h
->operation
= operation
;
76 h
->device_id
= PIMFOR_DEV_ID_MHLI_MIB
;
78 h
->oid
= cpu_to_be32(oid
);
79 h
->length
= cpu_to_be32(length
);
83 * Helper function to analyze a PIMFOR management frame header.
85 static pimfor_header_t
*
86 pimfor_decode_header(void *data
, int len
)
88 pimfor_header_t
*h
= data
;
90 while ((void *) h
< data
+ len
) {
91 if (h
->flags
& PIMFOR_FLAG_LITTLE_ENDIAN
) {
92 le32_to_cpus(&h
->oid
);
93 le32_to_cpus(&h
->length
);
95 be32_to_cpus(&h
->oid
);
96 be32_to_cpus(&h
->length
);
98 if (h
->oid
!= OID_INL_TUNNEL
)
106 * Fill the receive queue for management frames with fresh buffers.
109 islpci_mgmt_rx_fill(struct net_device
*ndev
)
111 islpci_private
*priv
= netdev_priv(ndev
);
112 isl38xx_control_block
*cb
= /* volatile not needed */
113 (isl38xx_control_block
*) priv
->control_block
;
114 u32 curr
= le32_to_cpu(cb
->driver_curr_frag
[ISL38XX_CB_RX_MGMTQ
]);
116 #if VERBOSE > SHOW_ERROR_MESSAGES
117 DEBUG(SHOW_FUNCTION_CALLS
, "islpci_mgmt_rx_fill\n");
120 while (curr
- priv
->index_mgmt_rx
< ISL38XX_CB_MGMT_QSIZE
) {
121 u32 index
= curr
% ISL38XX_CB_MGMT_QSIZE
;
122 struct islpci_membuf
*buf
= &priv
->mgmt_rx
[index
];
123 isl38xx_fragment
*frag
= &cb
->rx_data_mgmt
[index
];
125 if (buf
->mem
== NULL
) {
126 buf
->mem
= kmalloc(MGMT_FRAME_SIZE
, GFP_ATOMIC
);
129 "Error allocating management frame.\n");
132 buf
->size
= MGMT_FRAME_SIZE
;
134 if (buf
->pci_addr
== 0) {
135 buf
->pci_addr
= pci_map_single(priv
->pdev
, buf
->mem
,
138 if (!buf
->pci_addr
) {
140 "Failed to make memory DMA'able.\n");
145 /* be safe: always reset control block information */
146 frag
->size
= cpu_to_le16(MGMT_FRAME_SIZE
);
148 frag
->address
= cpu_to_le32(buf
->pci_addr
);
151 /* The fragment address in the control block must have
152 * been written before announcing the frame buffer to
155 cb
->driver_curr_frag
[ISL38XX_CB_RX_MGMTQ
] = cpu_to_le32(curr
);
161 * Create and transmit a management frame using "operation" and "oid",
162 * with arguments data/length.
163 * We either return an error and free the frame, or we return 0 and
164 * islpci_mgt_cleanup_transmit() frees the frame in the tx-done
168 islpci_mgt_transmit(struct net_device
*ndev
, int operation
, unsigned long oid
,
169 void *data
, int length
)
171 islpci_private
*priv
= netdev_priv(ndev
);
172 isl38xx_control_block
*cb
=
173 (isl38xx_control_block
*) priv
->control_block
;
177 isl38xx_fragment
*frag
;
178 struct islpci_membuf buf
;
181 int frag_len
= length
+ PIMFOR_HEADER_SIZE
;
183 #if VERBOSE > SHOW_ERROR_MESSAGES
184 DEBUG(SHOW_FUNCTION_CALLS
, "islpci_mgt_transmit\n");
187 if (frag_len
> MGMT_FRAME_SIZE
) {
188 printk(KERN_DEBUG
"%s: mgmt frame too large %d\n",
189 ndev
->name
, frag_len
);
194 p
= buf
.mem
= kmalloc(frag_len
, GFP_KERNEL
);
196 printk(KERN_DEBUG
"%s: cannot allocate mgmt frame\n",
202 /* create the header directly in the fragment data area */
203 pimfor_encode_header(operation
, oid
, length
, (pimfor_header_t
*) p
);
204 p
+= PIMFOR_HEADER_SIZE
;
207 memcpy(p
, data
, length
);
209 memset(p
, 0, length
);
211 #if VERBOSE > SHOW_ERROR_MESSAGES
213 pimfor_header_t
*h
= buf
.mem
;
214 DEBUG(SHOW_PIMFOR_FRAMES
,
215 "PIMFOR: op %i, oid 0x%08lx, device %i, flags 0x%x length 0x%x\n",
216 h
->operation
, oid
, h
->device_id
, h
->flags
, length
);
218 /* display the buffer contents for debugging */
219 display_buffer((char *) h
, sizeof (pimfor_header_t
));
220 display_buffer(p
, length
);
225 buf
.pci_addr
= pci_map_single(priv
->pdev
, buf
.mem
, frag_len
,
228 printk(KERN_WARNING
"%s: cannot map PCI memory for mgmt\n",
233 /* Protect the control block modifications against interrupts. */
234 spin_lock_irqsave(&priv
->slock
, flags
);
235 curr_frag
= le32_to_cpu(cb
->driver_curr_frag
[ISL38XX_CB_TX_MGMTQ
]);
236 if (curr_frag
- priv
->index_mgmt_tx
>= ISL38XX_CB_MGMT_QSIZE
) {
237 printk(KERN_WARNING
"%s: mgmt tx queue is still full\n",
242 /* commit the frame to the tx device queue */
243 index
= curr_frag
% ISL38XX_CB_MGMT_QSIZE
;
244 priv
->mgmt_tx
[index
] = buf
;
245 frag
= &cb
->tx_data_mgmt
[index
];
246 frag
->size
= cpu_to_le16(frag_len
);
247 frag
->flags
= 0; /* for any other than the last fragment, set to 1 */
248 frag
->address
= cpu_to_le32(buf
.pci_addr
);
250 /* The fragment address in the control block must have
251 * been written before announcing the frame buffer to
254 cb
->driver_curr_frag
[ISL38XX_CB_TX_MGMTQ
] = cpu_to_le32(curr_frag
+ 1);
255 spin_unlock_irqrestore(&priv
->slock
, flags
);
257 /* trigger the device */
258 islpci_trigger(priv
);
262 spin_unlock_irqrestore(&priv
->slock
, flags
);
270 * Receive a management frame from the device.
271 * This can be an arbitrary number of traps, and at most one response
272 * frame for a previous request sent via islpci_mgt_transmit().
275 islpci_mgt_receive(struct net_device
*ndev
)
277 islpci_private
*priv
= netdev_priv(ndev
);
278 isl38xx_control_block
*cb
=
279 (isl38xx_control_block
*) priv
->control_block
;
282 #if VERBOSE > SHOW_ERROR_MESSAGES
283 DEBUG(SHOW_FUNCTION_CALLS
, "islpci_mgt_receive\n");
286 /* Only once per interrupt, determine fragment range to
287 * process. This avoids an endless loop (i.e. lockup) if
288 * frames come in faster than we can process them. */
289 curr_frag
= le32_to_cpu(cb
->device_curr_frag
[ISL38XX_CB_RX_MGMTQ
]);
292 for (; priv
->index_mgmt_rx
< curr_frag
; priv
->index_mgmt_rx
++) {
293 pimfor_header_t
*header
;
294 u32 index
= priv
->index_mgmt_rx
% ISL38XX_CB_MGMT_QSIZE
;
295 struct islpci_membuf
*buf
= &priv
->mgmt_rx
[index
];
298 struct islpci_mgmtframe
*frame
;
300 /* I have no idea (and no documentation) if flags != 0
301 * is possible. Drop the frame, reuse the buffer. */
302 if (le16_to_cpu(cb
->rx_data_mgmt
[index
].flags
) != 0) {
303 printk(KERN_WARNING
"%s: unknown flags 0x%04x\n",
305 le16_to_cpu(cb
->rx_data_mgmt
[index
].flags
));
309 /* The device only returns the size of the header(s) here. */
310 frag_len
= le16_to_cpu(cb
->rx_data_mgmt
[index
].size
);
313 * We appear to have no way to tell the device the
314 * size of a receive buffer. Thus, if this check
315 * triggers, we likely have kernel heap corruption. */
316 if (frag_len
> MGMT_FRAME_SIZE
) {
318 "%s: Bogus packet size of %d (%#x).\n",
319 ndev
->name
, frag_len
, frag_len
);
320 frag_len
= MGMT_FRAME_SIZE
;
323 /* Ensure the results of device DMA are visible to the CPU. */
324 pci_dma_sync_single_for_cpu(priv
->pdev
, buf
->pci_addr
,
325 buf
->size
, PCI_DMA_FROMDEVICE
);
327 /* Perform endianess conversion for PIMFOR header in-place. */
328 header
= pimfor_decode_header(buf
->mem
, frag_len
);
330 printk(KERN_WARNING
"%s: no PIMFOR header found\n",
335 /* The device ID from the PIMFOR packet received from
336 * the MVC is always 0. We forward a sensible device_id.
337 * Not that anyone upstream would care... */
338 header
->device_id
= priv
->ndev
->ifindex
;
340 #if VERBOSE > SHOW_ERROR_MESSAGES
341 DEBUG(SHOW_PIMFOR_FRAMES
,
342 "PIMFOR: op %i, oid 0x%08x, device %i, flags 0x%x length 0x%x\n",
343 header
->operation
, header
->oid
, header
->device_id
,
344 header
->flags
, header
->length
);
346 /* display the buffer contents for debugging */
347 display_buffer((char *) header
, PIMFOR_HEADER_SIZE
);
348 display_buffer((char *) header
+ PIMFOR_HEADER_SIZE
,
352 /* nobody sends these */
353 if (header
->flags
& PIMFOR_FLAG_APPLIC_ORIGIN
) {
355 "%s: errant PIMFOR application frame\n",
360 /* Determine frame size, skipping OID_INL_TUNNEL headers. */
361 size
= PIMFOR_HEADER_SIZE
+ header
->length
;
362 frame
= kmalloc(sizeof (struct islpci_mgmtframe
) + size
,
366 "%s: Out of memory, cannot handle oid 0x%08x\n",
367 ndev
->name
, header
->oid
);
371 memcpy(&frame
->buf
, header
, size
);
372 frame
->header
= (pimfor_header_t
*) frame
->buf
;
373 frame
->data
= frame
->buf
+ PIMFOR_HEADER_SIZE
;
375 #if VERBOSE > SHOW_ERROR_MESSAGES
376 DEBUG(SHOW_PIMFOR_FRAMES
,
377 "frame: header: %p, data: %p, size: %d\n",
378 frame
->header
, frame
->data
, size
);
381 if (header
->operation
== PIMFOR_OP_TRAP
) {
382 #if VERBOSE > SHOW_ERROR_MESSAGES
384 "TRAP: oid 0x%x, device %i, flags 0x%x length %i\n",
385 header
->oid
, header
->device_id
, header
->flags
,
389 /* Create work to handle trap out of interrupt
391 INIT_WORK(&frame
->ws
, prism54_process_trap
);
392 schedule_work(&frame
->ws
);
395 /* Signal the one waiting process that a response
396 * has been received. */
397 if ((frame
= xchg(&priv
->mgmt_received
, frame
)) != NULL
) {
399 "%s: mgmt response not collected\n",
403 #if VERBOSE > SHOW_ERROR_MESSAGES
404 DEBUG(SHOW_TRACING
, "Wake up Mgmt Queue\n");
406 wake_up(&priv
->mgmt_wqueue
);
415 * Cleanup the transmit queue by freeing all frames handled by the device.
418 islpci_mgt_cleanup_transmit(struct net_device
*ndev
)
420 islpci_private
*priv
= netdev_priv(ndev
);
421 isl38xx_control_block
*cb
= /* volatile not needed */
422 (isl38xx_control_block
*) priv
->control_block
;
425 #if VERBOSE > SHOW_ERROR_MESSAGES
426 DEBUG(SHOW_FUNCTION_CALLS
, "islpci_mgt_cleanup_transmit\n");
429 /* Only once per cleanup, determine fragment range to
430 * process. This avoids an endless loop (i.e. lockup) if
431 * the device became confused, incrementing device_curr_frag
433 curr_frag
= le32_to_cpu(cb
->device_curr_frag
[ISL38XX_CB_TX_MGMTQ
]);
436 for (; priv
->index_mgmt_tx
< curr_frag
; priv
->index_mgmt_tx
++) {
437 int index
= priv
->index_mgmt_tx
% ISL38XX_CB_MGMT_QSIZE
;
438 struct islpci_membuf
*buf
= &priv
->mgmt_tx
[index
];
439 pci_unmap_single(priv
->pdev
, buf
->pci_addr
, buf
->size
,
449 * Perform one request-response transaction to the device.
452 islpci_mgt_transaction(struct net_device
*ndev
,
453 int operation
, unsigned long oid
,
454 void *senddata
, int sendlen
,
455 struct islpci_mgmtframe
**recvframe
)
457 islpci_private
*priv
= netdev_priv(ndev
);
458 const long wait_cycle_jiffies
= msecs_to_jiffies(ISL38XX_WAIT_CYCLE
* 10);
459 long timeout_left
= ISL38XX_MAX_WAIT_CYCLES
* wait_cycle_jiffies
;
465 if (mutex_lock_interruptible(&priv
->mgmt_lock
))
468 prepare_to_wait(&priv
->mgmt_wqueue
, &wait
, TASK_UNINTERRUPTIBLE
);
469 err
= islpci_mgt_transmit(ndev
, operation
, oid
, senddata
, sendlen
);
474 while (timeout_left
> 0) {
476 struct islpci_mgmtframe
*frame
;
478 timeleft
= schedule_timeout_uninterruptible(wait_cycle_jiffies
);
479 frame
= xchg(&priv
->mgmt_received
, NULL
);
481 if (frame
->header
->oid
== oid
) {
487 "%s: expecting oid 0x%x, received 0x%x.\n",
488 ndev
->name
, (unsigned int) oid
,
496 "%s: timeout waiting for mgmt response %lu, "
497 "triggering device\n",
498 ndev
->name
, timeout_left
);
499 islpci_trigger(priv
);
501 timeout_left
+= timeleft
- wait_cycle_jiffies
;
503 printk(KERN_WARNING
"%s: timeout waiting for mgmt response\n",
506 /* TODO: we should reset the device here */
508 finish_wait(&priv
->mgmt_wqueue
, &wait
);
509 mutex_unlock(&priv
->mgmt_lock
);