Esempio n. 1
0
int islpci_eth_transmit(struct sk_buff *skb, struct net_device *nwdev)
{
    islpci_private *private_config = nwdev->priv;
    isl38xx_control_block *control_block = private_config->control_block;
    isl38xx_fragment *fragment;
    u32 index, counter, pci_map_address;
    int fragments;
    int frame_size;
    int offset;
    struct sk_buff *newskb;
    int newskb_offset;
	unsigned long flags;
	unsigned char wds_mac[6];
#if VERBOSE > SHOW_ERROR_MESSAGES 
    DEBUG(SHOW_FUNCTION_CALLS, "islpci_eth_transmit \n");
#endif

    // lock the driver code
    driver_lock( &private_config->slock, &flags );

    // check whether the deviec is running
//      if( !netif_running( nwdev ) )
//      {
//              DEBUG( SHOW_ERROR_MESSAGES, "%s: Tx on stopped device!\n",
//                     nwdev->name);
//              return 1;
//      }

//      tx_timeout_check( nwdev, islpci_eth_tx_timeout );
//      skb_tx_check( nwdev, skb );

    // determine the amount of fragments needed to store the frame
    memset (wds_mac, 0, 6);
#ifdef WDS_LINKS
    check_skb_for_wds(skb, wds_mac);
#ifdef ISLPCI_ETH_DEBUG
    if ( wds_mac[0] || wds_mac[1] || wds_mac[2] || wds_mac[3] || wds_mac[4] || wds_mac[5] ) {
        printk("islpci_eth_transmit:check_skb_for_wds ! %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", 
               wds_mac[0], wds_mac[1], wds_mac[2], wds_mac[3], wds_mac[4], wds_mac[5] );
    } else {
        printk("islpci_eth_transmit:check_skb_for_wds %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", 
               wds_mac[0], wds_mac[1], wds_mac[2], wds_mac[3], wds_mac[4], wds_mac[5] );
    }
#endif
#endif
    
    frame_size = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
    if( init_wds ) frame_size += 6;
    fragments = frame_size / MAX_FRAGMENT_SIZE;
    fragments += frame_size % MAX_FRAGMENT_SIZE == 0 ? 0 : 1;


#if VERBOSE > SHOW_ERROR_MESSAGES 
    DEBUG(SHOW_TRACING, "Fragments needed for frame %i\n", fragments);
#endif

    // check whether the destination queue has enough fragments for the frame
    if (ISL38XX_CB_TX_QSIZE - isl38xx_in_queue( control_block, 
		ISL38XX_CB_TX_DATA_LQ ) < fragments )
    {
        // Error, cannot add the frame because the queue is full
        DEBUG(SHOW_ERROR_MESSAGES, "Error: Queue [%i] not enough room\n",
	        ISL38XX_CB_TX_DATA_LQ);

        // trigger the device by setting the Update bit in the Device Int reg
        writel(ISL38XX_DEV_INT_UPDATE, private_config->remapped_device_base + 
            ISL38XX_DEV_INT_REG);
        udelay(ISL38XX_WRITEIO_DELAY);

        // unlock the driver code
        driver_unlock( &private_config->slock, &flags );
        return -EBUSY;
    }

    // Check alignment and WDS frame formatting. The start of the packet should
    // be aligned on a 4-byte boundary. If WDS is enabled add another 6 bytes
    // and add WDS address information
    if( ((int) skb->data & 0x03) | init_wds )
    {
        // get the number of bytes to add and re-allign
        offset = (int) skb->data & 0x03;
		offset += init_wds ? 6 : 0;		

		// check whether the current skb can be used 
        if (!skb_cloned(skb) && (skb_tailroom(skb) >= offset))
        {
            unsigned char *src = skb->data;

#if VERBOSE > SHOW_ERROR_MESSAGES 
	        DEBUG(SHOW_TRACING, "skb offset %i wds %i\n", offset, init_wds );
#endif

			// allign the buffer on 4-byte boundary
            skb_reserve(skb, (int) skb->data & 0x03 );
            if( init_wds )
            {
            	// wds requires an additional address field of 6 bytes
				skb_put( skb, 6 ); 
#ifdef ISLPCI_ETH_DEBUG
                printk("islpci_eth_transmit:wds_mac\n" );
#endif			    
                memmove( skb->data+6, src, skb->len );
                memcpy( skb->data, wds_mac, 6 );
            }
            else
			{
                memmove( skb->data, src, skb->len );
			}
			
#if VERBOSE > SHOW_ERROR_MESSAGES 
            DEBUG(SHOW_TRACING, "memmove %p %p %i \n", skb->data, src,
                skb->len );
#endif
        }
        else
        {
            newskb = dev_alloc_skb( init_wds ? skb->len+6 : skb->len );
            newskb_offset = (int) newskb->data & 0x03;

            // Check if newskb->data is aligned
            if( newskb_offset )
                skb_reserve(newskb, newskb_offset);

            skb_put(newskb, init_wds ? skb->len+6 : skb->len );
            if( init_wds )
            {
                memcpy( newskb->data+6, skb->data, skb->len );
                memcpy( newskb->data, wds_mac, 6 );
#ifdef ISLPCI_ETH_DEBUG
                printk("islpci_eth_transmit:wds_mac\n" );
#endif
            }
            else
                memcpy( newskb->data, skb->data, skb->len );

#if VERBOSE > SHOW_ERROR_MESSAGES 
            DEBUG(SHOW_TRACING, "memcpy %p %p %i wds %i\n", newskb->data,
                skb->data, skb->len, init_wds );
#endif

            newskb->dev = skb->dev;
            dev_kfree_skb(skb);
            skb = newskb;
        }
    }

    // display the buffer contents for debugging
#if VERBOSE > SHOW_ERROR_MESSAGES 
    DEBUG( SHOW_BUFFER_CONTENTS, "\ntx %p ", skb->data );
    display_buffer((char *) skb->data, skb->len );
#endif

	// map the skb buffer to pci memory for DMA operation
	pci_map_address = pci_map_single( private_config->pci_device, 
		(void *) skb->data, skb->len, PCI_DMA_TODEVICE );
    if( pci_map_address == (dma_addr_t) NULL )
	{
    	// error mapping the buffer to device accessable memory address
	    DEBUG(SHOW_ERROR_MESSAGES, "Error map DMA addr, data lost\n" );

	    // free the skbuf structure before aborting
    	dev_kfree_skb(skb);

        // unlock the driver code
        driver_unlock( &private_config->slock, &flags );
	    return -EIO;
    }

	// place each fragment in the control block structure and store in the last
	// needed fragment entry of the pci_map_tx_address and data_low_tx arrays 
	// the skb frame information
    for (counter = 0; counter < fragments; counter++ )
	{
		// get a pointer to the target control block fragment
	    index = (counter + le32_to_cpu(
	        control_block->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ])) %
    	    ISL38XX_CB_TX_QSIZE;			
	    fragment = &control_block->tx_data_low[index];
	
		// check whether this frame fragment is the last one
		if( counter == fragments-1 )
		{
			// the fragment is the last one, add the streaming DMA mapping for 
			// proper PCI bus operation
    		private_config->pci_map_tx_address[index] = 
                (dma_addr_t) pci_map_address;
			
			// store the skb address for future freeing 
			private_config->data_low_tx[index] = skb;
		}
		else
		{
			// the fragment will be followed by more fragments
			// clear the pci_map_tx_address and data_low_tx entries
		    private_config->pci_map_tx_address[index] = (dma_addr_t) NULL;
			private_config->data_low_tx[index] = NULL;
		}
			
		// set the proper fragment start address and size information
        fragment->address = cpu_to_le32( pci_map_address + 
			counter*MAX_FRAGMENT_SIZE );
        fragment->size = cpu_to_le16( frame_size > MAX_FRAGMENT_SIZE ?
            MAX_FRAGMENT_SIZE : frame_size );
        fragment->flags = cpu_to_le16( frame_size > MAX_FRAGMENT_SIZE ? 1 : 0 );
        frame_size -= MAX_FRAGMENT_SIZE;
	}

	// ready loading all fragements to the control block and setup pci mapping
	// update the control block interface information
    add_le32p(&control_block->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ],
        fragments);

    // check whether the data tx queue is full now
    if( ISL38XX_CB_TX_QSIZE - isl38xx_in_queue( control_block, 
		ISL38XX_CB_TX_DATA_LQ ) < ISL38XX_MIN_QTHRESHOLD )
    {
        // stop the transmission of network frames to the driver
        netif_stop_queue(nwdev);

        // set the full flag for the data low transmission queue
        private_config->data_low_tx_full = 1;
    }

    // trigger the device
    isl38xx_trigger_device( &private_config->powerstate,
        private_config->remapped_device_base );

    // set the transmission time
    nwdev->trans_start = jiffies;
    private_config->statistics.tx_packets++;
    private_config->statistics.tx_bytes += skb->len;

    // cleanup the data low transmit queue
    islpci_eth_cleanup_transmit( private_config, private_config->control_block );

    // unlock the driver code
    driver_unlock( &private_config->slock, &flags );
    return 0;
}
Esempio n. 2
0
int
islpci_eth_receive(islpci_private *priv)
{
	struct net_device *ndev = priv->ndev;
	isl38xx_control_block *control_block = priv->control_block;
	struct sk_buff *skb;
	u16 size;
	u32 index, offset;
	unsigned char *src;
	int discard = 0;

#if VERBOSE > SHOW_ERROR_MESSAGES
	DEBUG(SHOW_FUNCTION_CALLS, "islpci_eth_receive \n");
#endif

	/* the device has written an Ethernet frame in the data area
	 * of the sk_buff without updating the structure, do it now */
	index = priv->free_data_rx % ISL38XX_CB_RX_QSIZE;
	size = le16_to_cpu(control_block->rx_data_low[index].size);
	skb = priv->data_low_rx[index];
	offset = ((unsigned long)
		  le32_to_cpu(control_block->rx_data_low[index].address) -
		  (unsigned long) skb->data) & 3;

#if VERBOSE > SHOW_ERROR_MESSAGES
	DEBUG(SHOW_TRACING,
	      "frq->addr %x skb->data %p skb->len %u offset %u truesize %u\n ",
	      control_block->rx_data_low[priv->free_data_rx].address, skb->data,
	      skb->len, offset, skb->truesize);
#endif

	/* delete the streaming DMA mapping before processing the skb */
	pci_unmap_single(priv->pdev,
			 priv->pci_map_rx_address[index],
			 MAX_FRAGMENT_SIZE_RX + 2, PCI_DMA_FROMDEVICE);

	/* update the skb structure and allign the buffer */
	skb_put(skb, size);
	if (offset) {
		/* shift the buffer allocation offset bytes to get the right frame */
		skb_pull(skb, 2);
		skb_put(skb, 2);
	}
#if VERBOSE > SHOW_ERROR_MESSAGES
	/* display the buffer contents for debugging */
	DEBUG(SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data);
	display_buffer((char *) skb->data, skb->len);
#endif

	/* check whether WDS is enabled and whether the data frame is a WDS frame */

	if (init_wds) {
		/* WDS enabled, check for the wds address on the first 6 bytes of the buffer */
		src = skb->data + 6;
		memmove(skb->data, src, skb->len - 6);
		skb_trim(skb, skb->len - 6);
	}
#if VERBOSE > SHOW_ERROR_MESSAGES
	DEBUG(SHOW_TRACING, "Fragment size %i in skb at %p\n", size, skb);
	DEBUG(SHOW_TRACING, "Skb data at %p, length %i\n", skb->data, skb->len);

	/* display the buffer contents for debugging */
	DEBUG(SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data);
	display_buffer((char *) skb->data, skb->len);
#endif
	/* take care of monitor mode and spy monitoring. */
	if (unlikely(priv->iw_mode == IW_MODE_MONITOR))
		discard = islpci_monitor_rx(priv, &skb);
	else {
		if (unlikely(skb->data[2 * ETH_ALEN] == 0)) {
			/* The packet has a rx_annex. Read it for spy monitoring, Then
			 * remove it, while keeping the 2 leading MAC addr.
			 */
			struct iw_quality wstats;
			struct rx_annex_header *annex =
			    (struct rx_annex_header *) skb->data;
			wstats.level = annex->rfmon.rssi;
			/* The noise value can be a bit outdated if nobody's
			 * reading wireless stats... */
			wstats.noise = priv->local_iwstatistics.qual.noise;
			wstats.qual = wstats.level - wstats.noise;
			wstats.updated = 0x07;
			/* Update spy records */
			wireless_spy_update(ndev, annex->addr2, &wstats);

			memcpy(skb->data + sizeof (struct rfmon_header),
			       skb->data, 2 * ETH_ALEN);
			skb_pull(skb, sizeof (struct rfmon_header));
		}
		skb->protocol = eth_type_trans(skb, ndev);
	}
	skb->ip_summed = CHECKSUM_NONE;
	priv->statistics.rx_packets++;
	priv->statistics.rx_bytes += size;

	/* deliver the skb to the network layer */
#ifdef ISLPCI_ETH_DEBUG
	printk
	    ("islpci_eth_receive:netif_rx %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
	     skb->data[0], skb->data[1], skb->data[2], skb->data[3],
	     skb->data[4], skb->data[5]);
#endif
	if (unlikely(discard)) {
		dev_kfree_skb_irq(skb);
		skb = NULL;
	} else
		netif_rx(skb);

	/* increment the read index for the rx data low queue */
	priv->free_data_rx++;

	/* add one or more sk_buff structures */
	while (index =
	       le32_to_cpu(control_block->
			   driver_curr_frag[ISL38XX_CB_RX_DATA_LQ]),
	       index - priv->free_data_rx < ISL38XX_CB_RX_QSIZE) {
		/* allocate an sk_buff for received data frames storage
		 * include any required allignment operations */
		skb = dev_alloc_skb(MAX_FRAGMENT_SIZE_RX + 2);
		if (unlikely(skb == NULL)) {
			/* error allocating an sk_buff structure elements */
			DEBUG(SHOW_ERROR_MESSAGES, "Error allocating skb \n");
			break;
		}
		skb_reserve(skb, (4 - (long) skb->data) & 0x03);
		/* store the new skb structure pointer */
		index = index % ISL38XX_CB_RX_QSIZE;
		priv->data_low_rx[index] = skb;

#if VERBOSE > SHOW_ERROR_MESSAGES
		DEBUG(SHOW_TRACING,
		      "new alloc skb %p skb->data %p skb->len %u index %u truesize %u\n ",
		      skb, skb->data, skb->len, index, skb->truesize);
#endif

		/* set the streaming DMA mapping for proper PCI bus operation */
		priv->pci_map_rx_address[index] =
		    pci_map_single(priv->pdev, (void *) skb->data,
				   MAX_FRAGMENT_SIZE_RX + 2,
				   PCI_DMA_FROMDEVICE);
		if (unlikely(priv->pci_map_rx_address[index] == (dma_addr_t) NULL)) {
			/* error mapping the buffer to device accessable memory address */
			DEBUG(SHOW_ERROR_MESSAGES,
			      "Error mapping DMA address\n");

			/* free the skbuf structure before aborting */
			dev_kfree_skb_irq((struct sk_buff *) skb);
			skb = NULL;
			break;
		}
		/* update the fragment address */
		control_block->rx_data_low[index].address =
			cpu_to_le32((u32)priv->pci_map_rx_address[index]);
		wmb();

		/* increment the driver read pointer */
		add_le32p((u32 *) &control_block->
			  driver_curr_frag[ISL38XX_CB_RX_DATA_LQ], 1);
	}

	/* trigger the device */
	islpci_trigger(priv);

	return 0;
}
Esempio n. 3
0
int islpci_eth_receive(islpci_private * private_config)
{
    struct net_device *nw_device = private_config->my_module;
    struct net_device *wds_dev = NULL;
    struct wds_net_local *wds_lp;
    isl38xx_control_block *control_block = private_config->control_block;
#ifdef WDS_LINKS
    struct wds_priv *wdsp = private_config->wdsp;
#endif    
    struct sk_buff *skb;
    u16 size;
    u32 index, offset;
    unsigned char *src;

#if VERBOSE > SHOW_ERROR_MESSAGES 
    DEBUG(SHOW_FUNCTION_CALLS, "islpci_eth_receive \n");
#endif

    // the device has written an Ethernet frame in the data area
    // of the sk_buff without updating the structure, do it now
	index = private_config->free_data_rx % ISL38XX_CB_RX_QSIZE;
    size = le16_to_cpu(control_block->rx_data_low[index].size);
	skb = private_config->data_low_rx[index];
    offset = ((u32) le32_to_cpu(control_block->rx_data_low[index].address) - 
        (u32) skb->data ) & 3;

#if VERBOSE > SHOW_ERROR_MESSAGES 
    DEBUG( SHOW_TRACING, "frq->addr %x skb->data %p skb->len %u offset %u truesize %u\n ", 
        control_block->rx_data_low[private_config->free_data_rx].address,
		skb->data, skb->len, offset, skb->truesize );
#endif

    // delete the streaming DMA mapping before processing the skb
    pci_unmap_single( private_config->pci_device, 
        private_config->pci_map_rx_address[index],
        MAX_FRAGMENT_SIZE+2, PCI_DMA_FROMDEVICE );

	// update the skb structure and allign the buffer
    skb_put( skb, size );
    if( offset )
    {
		// shift the buffer allocation offset bytes to get the right frame
        skb_pull( skb, 2 );
		skb_put( skb, 2 );
    }
    
#if VERBOSE > SHOW_ERROR_MESSAGES 
    // display the buffer contents for debugging
    DEBUG( SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data );
    display_buffer((char *) skb->data, skb->len );
#endif

    // check whether WDS is enabled and whether the data frame is a WDS frame

    if( init_wds )
    {
        // WDS enabled, check for the wds address on the first 6 bytes of the buffer
#ifdef WDS_LINKS
        wds_dev = wds_find_device( skb->data, wdsp ); 
#ifdef ISLPCI_ETH_DEBUG
        if ( wds_dev ) {
            printk("islpci_eth_receive:wds_find_device ! %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", 
                   skb->data[0], skb->data[1], skb->data[2], skb->data[3], skb->data[4], skb->data[5] );
        } else {
            printk("islpci_eth_receive:wds_find_device %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", 
                   skb->data[0], skb->data[1], skb->data[2], skb->data[3], skb->data[4], skb->data[5] );
        }
#endif
#endif
        src = skb->data+6;
        memmove( skb->data, src, skb->len-6 );
        skb_trim( skb, skb->len-6 );
    }

#if VERBOSE > SHOW_ERROR_MESSAGES 
    DEBUG(SHOW_TRACING, "Fragment size %i in skb at %p\n", size, skb);
    DEBUG(SHOW_TRACING, "Skb data at %p, length %i\n", skb->data, skb->len);

    // display the buffer contents for debugging
    DEBUG( SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data );
    display_buffer((char *) skb->data, skb->len );
#endif

    // do some additional sk_buff and network layer parameters
    skb->dev = nw_device;
    skb->protocol = eth_type_trans(skb, nw_device);
    skb->ip_summed = CHECKSUM_NONE;
    private_config->statistics.rx_packets++;
    private_config->statistics.rx_bytes += size;

#ifdef WDS_LINKS
        if ( wds_dev != NULL )
        {
            wds_lp = (struct wds_net_local *) wds_dev->priv;
            wds_lp->stats.rx_packets++;
            wds_lp->stats.rx_bytes += skb->len;            
            skb->dev = wds_dev;
        }
#endif
    // deliver the skb to the network layer
#ifdef ISLPCI_ETH_DEBUG
    printk("islpci_eth_receive:netif_rx %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", 
           skb->data[0], skb->data[1], skb->data[2], skb->data[3], skb->data[4], skb->data[5] );
#endif
    netif_rx(skb);

    // increment the read index for the rx data low queue
    private_config->free_data_rx++;

    // add one or more sk_buff structures
    while( index = le32_to_cpu( 
		control_block->driver_curr_frag[ISL38XX_CB_RX_DATA_LQ] ), 
		index - private_config->free_data_rx < ISL38XX_CB_RX_QSIZE )
    {
        // allocate an sk_buff for received data frames storage
	    // include any required allignment operations
        if (skb = dev_alloc_skb(MAX_FRAGMENT_SIZE+2), skb == NULL)
        {
            // error allocating an sk_buff structure elements
            DEBUG(SHOW_ERROR_MESSAGES, "Error allocating skb \n");
            break;
        }

		// store the new skb structure pointer
		index = index % ISL38XX_CB_RX_QSIZE;
		private_config->data_low_rx[index] = skb;

#if VERBOSE > SHOW_ERROR_MESSAGES 
        DEBUG( SHOW_TRACING, "new alloc skb %p skb->data %p skb->len %u index %u truesize %u\n ", 
            skb, skb->data, skb->len, index, skb->truesize );
#endif

        // set the streaming DMA mapping for proper PCI bus operation
		private_config->pci_map_rx_address[index] = pci_map_single(
			private_config->pci_device, (void *) skb->data, MAX_FRAGMENT_SIZE+2, 
			PCI_DMA_FROMDEVICE );
        if( private_config->pci_map_rx_address[index] == (dma_addr_t) NULL )
        {
            // error mapping the buffer to device accessable memory address
            DEBUG(SHOW_ERROR_MESSAGES, "Error mapping DMA address\n");

            // free the skbuf structure before aborting
            dev_kfree_skb((struct sk_buff *) skb );
            break;
        }

        // update the fragment address
        control_block->rx_data_low[index].address = cpu_to_le32( (u32)
		 	private_config->pci_map_rx_address[index] );

        // increment the driver read pointer
        add_le32p(&control_block->driver_curr_frag[ISL38XX_CB_RX_DATA_LQ], 1 );
    }

    // trigger the device
    isl38xx_trigger_device( &private_config->powerstate,
        private_config->remapped_device_base );

    return 0;
}