/* * GigaSpaceWire over ethernet driver for MC-30SF6EM-6U evaluation kit * * Copyright (c) 2019 Elvees (support@elvees.com) * Author: Dmitry Evtushenko * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ethospw.h" #include "gspwsw.h" #include "multicore.h" #include "func.h" #include "reg.h" #define ETHOSPW_INTF_QNT 2 #define ETHOSPW_INTF0 0 #define ETHOSPW_INTF1 1 //#define DEV_NET_NAME "ethospw" //#define CARD_VERSION "MC24R2" #define CARD_VERSION "МС30SF6" #define MODULE_NAME "ethospw" #define DRIVER_VERSION "1.0" //#define PROC_FILENAME "spw_stat" #define ETH_MTU 2000 #define ETH_HEADER_SIZE 14 #define ETH_CRC_SIZE 4 // Devices: {ethospw0, ethospw1} // Net device ethospw0 base on gspwsw1 symbolic device, ethospw1 - gspwsw2 int is_init_ethospw = 0; //ethospw_t ethospw; static struct net_device *a_pndev[ETHOSPW_INTF_QNT] = { NULL, NULL }; //////////////////// skb_t: //////////////////// void skbd_clear( skb_dat_t *pskbd) { memset( pskbd, 0, sizeof(*pskbd)); } // Add data to the end of sk buffer: // sz_data - allocated data size // len_d - added data length int skbd_add_data( skb_dat_t *pskbd, ethospw_t *peos, u32 sz_data, char *pd, u32 len_d) { u32 len; if( pskbd->pskb == NULL) { // Выделяем sk буфер с выравниванием на 16 байт: pskbd->pskb = dev_alloc_skb( (sz_data + 16 + 0xF) & ~(0xF)); if (unlikely(pskbd->pskb == NULL)) { ++peos->pdev->stats.rx_dropped; return -1; } // Выравниваем заголовок IP на 8 байт: skb_reserve( pskbd->pskb, 8); pskbd->pdata = skb_put( pskbd->pskb, len); pskbd->sz_data = sz_data; pskbd->pos_data = 0; } // chip_read_rxfifo(u, mips_virt_to_phys ((unsigned) data), len); len = min( len_d, (pskbd->sz_data - pskbd->pos_data)); if( len) { memcpy( &pskbd->pdata[ pskbd->pos_data], pd, len); pskbd->pos_data += len; } return len; } int skbd_send( skb_dat_t *pskbd, ethospw_t *peos) { int rc; pskbd->pskb->protocol = eth_type_trans( pskbd->pskb, peos->pdev); rc = netif_rx( pskbd->pskb); if( rc == NET_RX_SUCCESS) { peos->pdev->stats.rx_packets++; peos->pdev->stats.rx_bytes += pskbd->sz_data; } return rc; // NET_RX_DROP } //////////////////// gspw_desc_t: //////////////////// void ethospw_init( ethospw_t *peos) { memset( peos, 0, sizeof(*peos)); } //////////////////// ethospw driver functions: //////////////////// // Setup MAC address: static int spw_set_mac_addr( struct net_device *dev, void *addr) { pr_info( "=== spw_set_mac_addr\n"); memcpy( dev->dev_addr, addr, dev->addr_len); return 0; } // Change MTU: static int spw_change_mtu( struct net_device *dev, int new_mtu) { pr_info( "=== spw_change_mtu\n"); if (new_mtu > ETH_MTU) return -EMSGSIZE; dev->mtu = new_mtu; return 0; } static netdev_tx_t spw_start_xmit (struct sk_buff *skb, struct net_device *dev) { ethospw_t *peos = netdev_priv(dev); u32 spw_num = peos->num - 1; gspw_ctrl_t *pspw = &gspw_sw.a_gspwc[spw_num]; loff_t pos = 0; ssize_t rc; pr_info( "=== spw_start_xmit: channel %d transmit %d bytes\n", peos->num, skb->len); #if 0 PDEBUG ("1. QSTR2 = %08X, TX DESC CSR = %08X, TX DESC IR = %08X, TX DATA CSR = %08X, RX DESC CSR = %08X\n", MC_QSTR2, MC_SWIC_TX_DESC_RUN(u->num), MC_SWIC_TX_DESC_IR(u->num), MC_SWIC_TX_DATA_RUN(u->num), MC_SWIC_RX_DESC_RUN(u->num)); if ( ! (MC_SWIC_STATUS(u->num) & MC_SWIC_CONNECTED) ) { PDEBUG ("spw_start_xmit: channel %d not connected, STATUS = %08X\n", u->num, MC_SWIC_STATUS(u->num)); ++dev->stats.tx_carrier_errors; dev_kfree_skb(skb); netif_stop_queue(dev); PDEBUG ("!!! RESTARTING\n"); spw_clean_dma (u); spw_start (u); /* if (! timer_pending (&u->start_timer)) { u->no_restart = 0; u->start_timer.expires = jiffies + 2; PDEBUG ("start_timer.function = %08X, data = %08X\n", u->start_timer.function, u->start_timer.data); add_timer (&u->start_timer); } */ return NETDEV_TX_OK; } PDEBUG (" spw_start_xmit: channel %d connected, STATUS = %08X\n", u->num, MC_SWIC_STATUS(u->num)); MC_SWIC_STATUS (u->num) |= 0xf; if (skb->len < 4 || skb->len > dev->mtu + ETH_HEADER_SIZE + ETH_CRC_SIZE) { PDEBUG (" spw_start_xmit: skb->len = %d, dev->mtu = %d\n", skb->len, dev->mtu); ++dev->stats.tx_errors; dev_kfree_skb(skb); return NETDEV_TX_OK; } disable_irq (spw_tx_data_irq (u->num)); if (MC_SWIC_TX_DATA_RUN (u->num) & 1) { // Busy, stopping tx queue PDEBUG ("\n\n\n\n\n spw_start_xmit: stop, wait for interrupt\n"); PDEBUG (" STATUS = %08X, TX DESC CSR = %08X, TX DATA CSR = %08X\n", MC_SWIC_STATUS(u->num), MC_SWIC_TX_DESC_RUN(u->num), MC_SWIC_TX_DATA_RUN(u->num)); u->pending_tx_skb = skb; netif_stop_queue(dev); /* if (! timer_pending (&u->start_timer)) { u->no_restart = 0; u->start_timer.expires = jiffies + 2; PDEBUG ("start_timer.function = %08X, data = %08X\n", u->start_timer.function, u->start_timer.data); add_timer (&u->start_timer); } */ } else { PDEBUGG (" spw_start_xmit: transmit now\n"); spw_transmit_packet (skb, dev); #ifdef RETRANS_ON u->recent_tx_skb = skb; #else dev_kfree_skb(skb); #endif } enable_irq (spw_tx_data_irq (u->num)); PDEBUGG ("2. QSTR2 = %08X, TX DESC CSR = %08X, TX DESC IR = %08X, TX DATA CSR = %08X\n", MC_QSTR2, MC_SWIC_TX_DESC_RUN(u->num), MC_SWIC_TX_DESC_IR(u->num), MC_SWIC_TX_DATA_RUN(u->num)); #endif //0 if( !pspw->connected ) { pr_info( "spw_start_xmit: spw channel %d not connected, packet is rejected\n", spw_num); ++dev->stats.tx_carrier_errors; dev_kfree_skb( skb); netif_stop_queue( dev); //PDEBUG ("!!! RESTARTING\n"); //spw_clean_dma (u); //spw_start (u); /* if (! timer_pending (&u->start_timer)) { u->no_restart = 0; u->start_timer.expires = jiffies + 2; PDEBUG ("start_timer.function = %08X, data = %08X\n", u->start_timer.function, u->start_timer.data); add_timer (&u->start_timer); } */ return NETDEV_TX_OK; } //pr_info( "spw_start_xmit: channel %d connected, STATUS = %08X\n", u->num, MC_SWIC_STATUS(u->num)); //MC_SWIC_STATUS (u->num) |= 0xf; if( skb->len < 4 || skb->len > dev->mtu + ETH_HEADER_SIZE + ETH_CRC_SIZE) { pr_info( "spw_start_xmit: Incorrect packet length %d\n", skb->len); ++dev->stats.tx_errors; dev_kfree_skb(skb); return NETDEV_TX_OK; } pr_info( "spw_start_xmit: transmit packet\n"); rc = gspw_dev_write( peos->num, 1, skb->data, skb->len, &pos); dev_kfree_skb(skb); if( rc==-EBUSY) return NETDEV_TX_BUSY; // if( rc==-ENODEV || rc==-ERESTARTSYS) - ? return NETDEV_TX_OK; } // ethospw rx job thread (for one on each channel): int thread_ethospw_rx_job( void *pv) { ethospw_t *peos = (ethospw_t *)pv; const char *intf_name = peos->pdev->name; u32 dev_num = peos->num; u32 spw_num = peos->num - 1; gspw_cfg_ctrl_t *pcfg = &gspw_sw.cfg_ctrl; gspw_ctrl_t *pspw = &gspw_sw.a_gspwc[spw_num]; dma_desc_fi_t *p_desc = NULL, *pdsk; dma_data_fi_t *p_data = NULL; dma_data_fi_t *p_data_free = NULL; dma_chain_t *pch_desc = &pcfg->dch_rx_desc; dma_chain_t *pch_data = &pcfg->dch_rx_data; fifo_t *pff_desc = &pch_desc->ff; fifo_t *pff_data = &pch_data->ff; int rc/*, i=-1*/; //u32 pos = 0; //u32 *ppos = &pos; //u32 sz_rd_avail = size - *ppos; //- returned buffer available size u32 sz_desc_data; u32 sz_data_buf_avail; u32 len_data; u32 cnt_rx = 0; //- returned data counter u32 ir; u32 len_cur_data; int has_new_rx_data; u32 ir_before_wait; u32 is_drop_pack = 0; if( (dev_num != GSPW_PORT_SWIC0) && (dev_num != GSPW_PORT_SWIC1)) { PRINT( "ethospw_rx_job: unsupportable port=%d\n", dev_num); return -ENODEV; } while(1) { if( peos->exit_rx_job) { peos->exit_rx_job = 2; break; } if (gspw_sw.mode != GSPW_MODE_WORK) { gspw_sw.is_rx_stop |= (1 << spw_num); goto l_wait; } if( pcfg->is_reset_io) goto l_wait; len_cur_data = 0; if( p_desc==NULL) { p_desc = (dma_desc_fi_t *)fifo_get_first_held( pff_desc); //if( p_desc) // pr_info( "drv_gspw_read: desc.port_mask=%Xh\n", p_desc->desc.port_mask); if( p_desc && (p_desc->desc.port_mask != dev_num)) p_desc = NULL; } if( p_data==NULL) p_data = (dma_data_fi_t *)fifo_get_first_held( pff_data); //PRINT( "drv_gspw_read: p_desc=0x%p, p_data=0x%p\n", p_desc, p_data); if( p_desc && p_data==NULL) { // обрабатываем еще незаполненный буфер: // p_data_free = (dma_data_fi_t *)fifo_get_first_free( pff_data); if( p_data_free==NULL) { if( cnt_rx) break; goto l_wait; } p_data = (dma_data_fi_t *)fifo_get_first_held( pff_data); if( p_data==NULL) p_data = p_data_free; else continue; ir = MC_IR_GSPW_RX_DAT; if( (ir >= p_data->reg_init.ir) && (ir < p_data->reg_init.ir + pch_data->sz_data_buf) ) { len_cur_data = ir - p_data->reg_init.ir; } } if( p_desc==NULL || p_data==NULL || (p_desc && !p_desc->desc.valid)) { if( cnt_rx) break; goto l_wait; } //PRINT("Receive spw packet:\n"); //dma_desc_fi_print( p_desc); //PRINT("RX_DATA_RUN=%Xh\n", SWIC_DMA_RUN( SWIC_RX_DATA_CHAN)); //sz_rd_avail = size - (*ppos + cnt_rx); sz_desc_data = p_desc->desc.size - p_desc->pos_proc; sz_data_buf_avail = (len_cur_data ? len_cur_data : pch_data->sz_data_buf) - p_data->pos_proc; //PRINT("drv_gspw_read: size=%u, szs: %u %u %u\n", // size, sz_rd_avail, sz_desc_data, sz_data_buf_avail); //len_data = min3( sz_rd_avail, sz_desc_data, sz_data_buf_avail); len_data = min( sz_desc_data, sz_data_buf_avail); //PRINT("drv_gspw_read: len_data=%u\n", len_data); if( len_data==0) goto l_wait; PRINT( "ethospw_rx_job(%s): copy_to_user(%u)\n", intf_name, len_data); PRINT( "ethospw_rx_job(%s): unused: %Xh %Xh\n", intf_name, p_desc->desc.unused, p_desc->desc.unused1); //copy_to_user( &p_buf[*ppos + cnt_rx], &p_data->p_buf[p_data->pos_proc], len_data); if( !is_drop_pack) { rc = skbd_add_data( &peos->skb, peos, p_desc->desc.size, &p_data->p_buf[p_data->pos_proc], len_data); if( rc < 0) { is_drop_pack = 1; pr_info( "ethospw_rx_job(%s): Could not allocate sk-buffer on interface\n", intf_name); } else if( rc != len_data) { pr_info( "ethospw_rx_job(%s): Could not copy all data to sk buffer\n", intf_name); // dev_kfree_skb_any( &peos->skb, SKB_REASON_DROPPED); } } cnt_rx += len_data; p_desc->pos_proc += len_data; p_data->pos_proc += len_data; if( p_data->pos_proc == pch_data->sz_data_buf) { //PRINT( "drv_gspw_read: p_data=%p,%p; stat=%u\n", p_data, p_data_free, p_data->stat); if( (p_data != p_data_free) || (p_data->stat == DMA_FI_STAT__FILLED)) { PRINT("ethospw_rx_job(%s): free_held data ir=%Xh\n", intf_name, p_data->reg_init.ir); dma_data_fi_reset_proc( p_data); if( p_data != (dma_data_fi_t *)fifo_free_first_held( pff_data)) PRINT( "ethospw_rx_job(%s): ERROR: Trying of using free said RX data list from several threads!\n", intf_name); p_data->p_prev->reg_init.csr |= SWIC_DMA_CSR_RUN; if( !(MC_RUN_GSPW_RX_DAT & SWIC_DMA_CSR_RUN)) MC_RUN_GSPW_RX_DAT |= SWIC_DMA_CSR_RUN; p_data = NULL; } else { // данные буфера переданы, хотя прерывания по завершению вычитывания буфера еще не было: PRINT( "ethospw_rx_job(%s): Interrupt is absent at full filled data buffer\n", intf_name); } } if( p_desc->pos_proc == p_desc->desc.size) { if( is_drop_pack) { ++pcfg->rx_drop_pack; pcfg->rx_drop_bytes += p_desc->desc.size; is_drop_pack = 0; } else { if( skbd_send( &peos->skb, peos) != NET_RX_SUCCESS) { dev_kfree_skb_any( peos->skb.pskb); ++pcfg->rx_drop_pack; pcfg->rx_drop_bytes += p_desc->desc.size; } } PRINT( "ethospw_rx_job(%s): free_held desc ir=%Xh\n", intf_name, p_desc->reg_init.ir); dma_desc_fi_reset_proc( p_desc); if( p_desc != (dma_desc_fi_t *)fifo_free_first_held( pff_desc)) PRINT( "ethospw_rx_job(%s): ERROR: Trying of using free said RX descriptor list from several threads!\n", intf_name); p_desc->p_prev->reg_init.csr |= SWIC_DMA_CSR_RUN; if( !(MC_RUN_GSPW_RX_DES & SWIC_DMA_CSR_RUN)) MC_RUN_GSPW_RX_DES |= SWIC_DMA_CSR_RUN; p_desc = NULL; } //print_dma_rx_desc_regs( "drv_gspw_read"); //print_dma_rx_data_regs( "drv_gspw_read"); //PRINT( "drv_gspw_read: QSTR0=%Xh, MASK0=%X\n", MC_QSTR0, MC_MASKR0); continue; l_wait:; wake_up_interruptible( &pcfg->wq_txdata); wake_up_interruptible( &pcfg->wq_txdesc); wake_up_interruptible( &pcfg->wq_txjob); wake_up_next_rx_thread( pff_desc, dev_num); PRINT( "\nethospw_rx_job(%s): wait_event_interruptible()\n", intf_name); print_fifo_held_qnt( pcfg, "Net_READ_wait"); ir_before_wait = MC_IR_GSPW_RX_DAT; if( peos->exit_rx_job) continue; if( (rc=wait_event_interruptible( pspw->wq_rx, ((pdsk=(dma_desc_fi_t *)fifo_get_first_held( pff_desc)) && (pdsk->desc.port_mask == dev_num)) || (fifo_get_first_held( pff_data) && (ir_before_wait != MC_IR_GSPW_RX_DAT)) || (gspw_sw.mode != GSPW_MODE_WORK) /*(has_new_rx_data = has_new_rx_data_in_cur_buf(p_data->reg_init.ir, remains))*/ ) )) { PRINT("ethospw_rx_job(%s): wait_event_interruptible -> (%i) -ERESTARTSYS\n", intf_name, rc); //disable_irq( spw_rx_desc_irq()); return -ERESTARTSYS; } has_new_rx_data = (ir_before_wait != MC_IR_GSPW_RX_DAT); PRINT( "ethospw_rx_job(%s): wait_event_interruptible() -> %Xh %Xh %i: %Xh %Xh\n", intf_name, (u32 )fifo_get_first_held( pff_desc), (u32 )fifo_get_first_held( pff_data), has_new_rx_data, ir_before_wait, MC_IR_GSPW_RX_DAT); } return 0; } static int ndo_ethospw_init( struct net_device *dev) { ethospw_t *peos = netdev_priv( dev); pr_info( "=== ethospw_init: channel %d\n", peos->num); // Свой адрес dev->addr_assign_type = NET_ADDR_RANDOM; random_ether_addr( dev->perm_addr); //eth_set_mac_addr( dev, dev->perm_addr); spw_set_mac_addr( dev, dev->perm_addr); // Максимальный размер кадра //_W( u, RG_RX_FR_MAXSIZE, ETH_MTU + ETH_HEADER_SIZE + ETH_CRC_SIZE); return 0; } static int ndo_ethospw_open( struct net_device *dev) { ethospw_t *peos = netdev_priv (dev); u32 dev_num = peos->num; u32 spw_num = dev_num; //gspw_cfg_ctrl_t *pcfg; gspw_ctrl_t *pspw; int rc; if( dev_num >= ETHOSPW_INTF_QNT) { pr_info( "ndo_ethospw_open: Panic: Attempt of opening a nonexistent device '%s%d'\n", ETHOSPW_DEV_NAME, dev_num); return -ENODEV; } if( peos->is_open) { pr_info( "Could not open '%s%d' interface, - it's opened already\n", ETHOSPW_DEV_NAME, dev_num); return -EBUSY; } pspw = &gspw_sw.a_gspwc[spw_num]; mutex_lock( &pspw->mtx_rx); if( !pspw->port.open) { pr_info( "Could not open '%s%d' interface, - phy spw interface '%s%d' is closed\n", ETHOSPW_DEV_NAME, dev_num, GSPW_DEV_NAME, spw_num); rc = -EPERM; goto l_err; } if( gspw_sw.mode != GSPW_MODE_WORK) { pr_info( "Could not open '%s%d' interface, - phy spw level doesn't have work mode\n", ETHOSPW_DEV_NAME, dev_num); rc = -EPERM; goto l_err; } pr_info( "=== ndo_ethospw_open( '%s%d'):\n", ETHOSPW_DEV_NAME, dev_num); if( netif_queue_stopped(dev)) netif_wake_queue(dev); netif_stop_queue( dev); //if( (rc = gspw_dev_open( peos->num, 1)) ) // return rc; // create RX thread: if( (peos->p_task_rx_job = kthread_run( &thread_ethospw_rx_job, peos, "ethospw__rx_job"))==NULL ) { if( IS_ERR( peos->p_task_rx_job)) { pr_info( "Could not create 'ethospw_rx_job' thread on channel %i\n", peos->num); rc = -ENOMEM; goto l_err; } } netif_start_queue( dev); #if 0 spw_struct_init (u); ret = request_irq (spw_rx_desc_irq (u->num), spw_dma_rx_desc_ih, 0 /*irq_flags*/, dev->name, dev); if (ret) return -EFAULT; ret = request_irq (spw_tx_data_irq (u->num), spw_dma_tx_data_ih, 0 /*irq_flags*/, dev->name, dev); if (ret) { free_irq (spw_rx_desc_irq (u->num), dev); return -EFAULT; } ret = request_irq (spw_link_irq (u->num), spw_connected_ih, 0, dev->name, dev); if (ret) { free_irq (spw_rx_desc_irq (u->num), dev); free_irq (spw_tx_data_irq (u->num), dev); return -EFAULT; } spw_clean_dma (u); spw_start (u); /* u->start_timer.expires = jiffies + 1; add_timer (&u->start_timer); */ mdelay (1000); PDEBUG (" MODE_CR = %08X, STATUS = %08X\n", MC_SWIC_MODE_CR(u->num), MC_SWIC_STATUS(u->num)); #endif //0 peos->is_open = 1; pspw->port.net_usage = 1; mutex_unlock( &pspw->mtx_rx); pr_info("=== ethospw_open done!\n"); return 0; l_err:; mutex_unlock( &pspw->mtx_rx); return rc; //return -EFAULT; } static int ndo_ethospw_close( struct net_device *dev) { ethospw_t *peos = netdev_priv (dev); u32 spw_num = peos->num; gspw_ctrl_t *pspw; int rc; if( !peos->is_open) return 0; pspw = &gspw_sw.a_gspwc[spw_num]; mutex_lock( &pspw->mtx_rx); pr_info( "=== ethospw_close: channel %d\n", peos->num); #if 0 free_irq (spw_rx_desc_irq (u->num), dev); free_irq (spw_tx_data_irq (u->num), dev); spw_stop (u); #endif //if( (rc=gspw_dev_close( peos->num, 1)) ) // return rc; if( !netif_queue_stopped( dev)) netif_stop_queue(dev); if( peos->exit_rx_job == 0) { peos->exit_rx_job = 1; wake_up_interruptible( &pspw->wq_rx); mdelay( 500); if( peos->exit_rx_job != 2) { if( peos->p_task_rx_job) { if( (rc = kthread_stop( peos->p_task_rx_job)) < 0) { pr_info( "ndo_ethospw_close: kthread_stop ERROR(%u)\n", rc); } peos->p_task_rx_job = NULL; } } } peos->exit_rx_job = 0; peos->is_open = 0; pspw->port.net_usage = 0; mutex_unlock( &pspw->mtx_rx); return 0; } int ndo_ethospw_ioctl( struct net_device *dev, struct ifreq *ifr, int cmd) { return 0; } #if 0 static int ethospw_probe(struct platform_device *pdev) { /* struct net_device *ndev; struct resource *r; struct mc_eth *u; int ret;*/ pr_info( ">>> ethospw_probe:\n"); #if 0 ndev = alloc_etherdev(sizeof(struct mc_eth)); if (!ndev) { printk("%s: could not allocate device.\n", ETHOSPW_CARD_NAME); ret = -ENOMEM; goto err_get_res; } ndev->dma = (unsigned char)-1; ndev->netdev_ops = &netdev_ops; ndev->ethtool_ops = &mc_ethtool_ops; ether_setup(ndev); ndev->mtu = ETH_MTU; u = netdev_priv(ndev); u->ndev = ndev; r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); if (r == NULL) { ret = -ENODEV; goto err_get_res; } /* kshubin u->eth_regs = devm_request_and_ioremap(&pdev->dev, r); if (!u->eth_regs) { ret = -ENOMEM; goto err_get_res; } */ u->eth_regs = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(u->eth_regs)) { ret = -ENOMEM; goto err_get_res; } r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "txdma"); if (r == NULL) { ret = -ENODEV; goto err_get_res; } /* kshubin u->dma_tx_regs = devm_request_and_ioremap(&pdev->dev, r); if (!u->dma_tx_regs) { */ u->dma_tx_regs = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(u->dma_tx_regs)) { ret = -ENOMEM; goto err_get_res; } r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rxdma"); if (r == NULL) { ret = -ENODEV; goto err_get_res; } /* u->dma_rx_regs = devm_request_and_ioremap(&pdev->dev, r); if (!u->dma_rx_regs) { */ u->dma_rx_regs = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(u->dma_rx_regs)) { ret = -ENOMEM; goto err_get_res; } PDEBUG("Registers at: %p, RX_DMA: %p, TX_DMA: %p\n", u->eth_regs, u->dma_rx_regs, u->dma_tx_regs); u->tx_irq = platform_get_irq_byname(pdev, "txframe_irq"); if (u->tx_irq < 0) { ret = -EINVAL; goto err_get_res; } u->rx_irq = platform_get_irq_byname(pdev, "rxframe_irq"); if (u->rx_irq < 0) { ret = -EINVAL; goto err_get_res; } ndev->irq = u->rx_irq; #ifdef CONFIG_MULTICORE_ETH_TXBUF_CRAM u->txbuf = (unsigned char *) CONFIG_MULTICORE_ETH_TXBUF_ADDR; u->txbuf_phys = CONFIG_MULTICORE_ETH_TXBUF_ADDR & 0x1FFFFFFF; #else u->txbuf = dmam_alloc_coherent(&pdev->dev, CONFIG_MULTICORE_ETH_TXBUF_SIZE, &u->txbuf_phys, GFP_KERNEL); if (!u->txbuf) { ret = -ENOMEM; goto err_get_res; } PDEBUG("txbuf @ %p, phys_addr = %08X\n", u->txbuf, u->txbuf_phys); #endif #ifdef CONFIG_MULTICORE_ETH_NAPI // Запуск NAPI netif_napi_add(ndev, &u->napi, eth_poll, 64); #endif u->msg_level = netif_msg_init(-1, MC_MSG_DEFAULT); ret = register_netdev(ndev); if (ret) { goto err_register; } //create_proc_read_entry (PROC_FILENAME, 0 /* default mode */, // kshubin // NULL /* parent dir */, read_proc, ndev /* client data */); //proc_create_data(PROC_FILENAME, 0 /* default mode */, NULL /* parent dir */, platform_set_drvdata(pdev, ndev); #endif //0 printk("ethospw_probe: ethospw found\n"); return 0; /* err_register: #ifdef CONFIG_MULTICORE_ETH_NAPI netif_napi_del(&u->napi); #endif free_netdev(ndev); err_get_res: return ret; */ } static int ethospw_remove(struct platform_device *pdev) { //struct net_device *ndev = platform_get_drvdata(pdev); pr_info( ">>> ethospw_remove:\n"); #if 0 eth_close(ndev); remove_proc_entry(PROC_FILENAME, NULL /* parent dir */); #endif return 0; } static struct platform_driver ethospw_driver = { .driver = { .name = ETHOSPW_CARD_NAME, .owner = THIS_MODULE, }, .probe = ethospw_probe, //.remove = __devexit_p(mc_eth_remove), .remove = ethospw_remove, }; module_platform_driver( ethospw_driver); MODULE_DESCRIPTION("Multicore Ethernet over Spacewire Switch driver"); MODULE_AUTHOR("Dmitry Evtushenko"); MODULE_LICENSE("GPL"); #endif //0 static const struct net_device_ops netdev_ops = { .ndo_init = ndo_ethospw_init, .ndo_open = ndo_ethospw_open, .ndo_stop = ndo_ethospw_close, .ndo_do_ioctl = ndo_ethospw_ioctl, .ndo_start_xmit = spw_start_xmit, .ndo_change_mtu = spw_change_mtu, .ndo_set_mac_address = spw_set_mac_addr, }; //static int __init drv_ethospw_init( void) int drv_ethospw_init( void) { struct net_device *ndev; ethospw_t *peos; //struct proc_dir_entry *proc_dir; //char proc_filename [8]; //char s_devname[IFNAMSIZ]; int ret; int i; pr_info( "=== drv_ethospw_init:\n"); for( i = 0; (i < ETHOSPW_INTF_QNT); ++i) { ndev = alloc_etherdev(sizeof( ethospw_t)); //ndev = alloc_etherdev( 0); if( ndev == NULL) { pr_info( "%s%d: failed to allocate device.\n", ETHOSPW_DEV_NAME, i); return -ENOMEM; } a_pndev[i] = ndev; ndev->dma = (unsigned char)-1; //ndev->irq = spw_rx_desc_irq (i); ndev->netdev_ops = &netdev_ops; snprintf( ndev->name, sizeof(ndev->name), "%s%d", ETHOSPW_DEV_NAME, i); ether_setup (ndev); ndev->mtu = ETH_MTU; ndev->addr_assign_type = NET_ADDR_RANDOM; random_ether_addr (ndev->perm_addr); spw_set_mac_addr (ndev, ndev->perm_addr); peos = netdev_priv( ndev); ethospw_init( peos); peos->pdev = ndev; peos->num = i; ret = register_netdev( ndev); if( ret) { pr_info( "%s%d: failed to register device.\n", ETHOSPW_DEV_NAME, i); free_netdev(ndev); return -ENODEV; } pr_info("drv_ethospw_init: dev_name='%s'\n", ndev->name); /* RTNL: assertion failed ... : sprintf( s_devname, "%s%d", ETHOSPW_CARD_NAME, i); if( dev_change_name( ndev, s_devname)) { pr_info( "%s%d: failed to device name changing.\n", ETHOSPW_DEV_NAME, i); free_netdev(ndev); return -ENODEV; } pr_info("drv_ethospw_init: new dev_name='%s'\n", (ndev ? ndev->name : "")); */ //int dev_change_name(struct net_device *dev, const char *newname) //sprintf( proc_filename, "spw%d", i); //sprintf( proc_filename, "%s%d", ETHOSPW_DEV_NAME, i); /*proc_dir = create_proc_entry (proc_filename, 0, NULL); if (proc_dir) { proc_dir->read_proc = read_proc; proc_dir->write_proc = write_proc; proc_dir->data = u; }*/ } pr_info( "=== drv_ethospw_init done!\n"); return 0; } //static void __exit drv_ethospw_exit( void) void drv_ethospw_exit( void) { int i; pr_info( "=== drv_ethospw_exit:\n"); for( i = 0; (i < ETHOSPW_INTF_QNT); ++i) { if( a_pndev[i]) { free_netdev( a_pndev[i]); a_pndev[i] = NULL; } } } //module_init( drv_ethospw_init); //module_exit( drv_ethospw_exit); // dev_change_net_namespace