//#define MULTICORE_DEBUG 1 //#define RETRANS_ON #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "multicore.h" #define CARD_NAME "mc_spw" #define CARD_VERSION "MC24R2" #define MODULE_NAME "mc_spw" #define DRIVER_VERSION "1.0" #define PROC_FILENAME "spw_stat" #define MC_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) #define NB_OF_CHANNELS 2 #define EOP 1 #define EEP 2 #define SWIC_RESET 0x00000079 #define SWIC_START 0x00000101 #define SWIC_RESET_ALL_IRQS 0x0001d00f #define START_SPEED 10 #define SPEED_AFTER_START 10 #define ETH_MTU 2000 #define ETH_HEADER_SIZE 14 #define ETH_CRC_SIZE 4 #define RX_DATA_BUFSZ 32768 #define RX_DESC_BUFSZ 1024 // Макросы для вычисления номеров прерываний #define IRQ_SHIFT 3 #define spw_err_irq(port_id) (IRQ_SW0_ERR + ((port_id) << IRQ_SHIFT)) #define spw_time_irq(port_id) (IRQ_SW0_TIME + ((port_id) << IRQ_SHIFT)) #define spw_link_irq(port_id) (IRQ_SW0_LINK + ((port_id) << IRQ_SHIFT)) #define spw_rx_data_irq(port_id) (IRQ_SW0_RX_DATA + ((port_id) << IRQ_SHIFT)) #define spw_rx_desc_irq(port_id) (IRQ_SW0_RX_DESC + ((port_id) << IRQ_SHIFT)) #define spw_tx_data_irq(port_id) (IRQ_SW0_TX_DATA + ((port_id) << IRQ_SHIFT)) #define spw_tx_desc_irq(port_id) (IRQ_SW0_TX_DESC + ((port_id) << IRQ_SHIFT)) // Формат дескриптора пакета typedef struct __attribute__((__packed__)) _spw_descriptor_t { unsigned size : 25; // Размер данных unsigned unused : 4; // Не используется unsigned type : 2; // Тип конца пакета: EOP или EEP unsigned valid : 1; // Признак заполнения дескриптора // действительными данными unsigned padding; } spw_descriptor_t; typedef struct __attribute__((__packed__)) _dma_params_t { uint32_t zero; uint32_t ir; uint32_t cp; uint32_t csr; } dma_params_t; struct mc_spw { int num; char name [sizeof (CARD_NAME) + 1]; struct net_device * pdev; struct sk_buff * pending_tx_skb; struct sk_buff * recent_tx_skb; //int no_restart; spw_descriptor_t tx_desc __attribute__ ((aligned (8))); char * tx_data_buffer; spw_descriptor_t * rx_desc_buffer; char * rx_data_buffer; spw_descriptor_t * cur_rx_desc; char * cur_rx_data; dma_params_t rx_desc_chain [RX_DESC_BUFSZ / sizeof (spw_descriptor_t)] __attribute__ ((aligned (8))); dma_params_t rx_data_chain __attribute__ ((aligned (8))); struct timer_list start_timer; unsigned speed; int just_connected; char txbuf [ETH_MTU + ETH_HEADER_SIZE + ETH_CRC_SIZE] __attribute__ ((aligned (8))); char rxbuf [RX_DATA_BUFSZ] __attribute__ ((aligned (8))); char rxdescbuf [RX_DESC_BUFSZ] __attribute__ ((aligned (8))); }; static void connect_swic (unsigned long data); #if 0 static void dump (unsigned addr, unsigned rows, unsigned columns) { unsigned i, j; unsigned *p = (unsigned *)addr; for (i = 0; i < rows; ++i) { for (j = 0; j < columns; ++j) { printk ("%08X ", *p++); } printk ("\n"); } } #endif extern u_int32_t mips_hpt_frequency; static void spw_start (struct mc_spw *u) { // Сброс контроллера MC_SWIC_MODE_CR(u->num) = SWIC_RESET; MC_SWIC_MODE_CR(u->num) = SWIC_START; // Начальные установки и пуск MC_SWIC_TX_SPEED(u->num) = MC_SWIC_TX_SPEED_PRM(START_SPEED / 5) | MC_SWIC_PLL_TX_EN | MC_SWIC_LVDS_EN; MC_SWIC_MODE_CR(u->num) = MC_SWIC_LinkStart | MC_SWIC_AutoStart | MC_SWIC_WORK_TYPE | MC_SWIC_LINK_MASK; // Сброс всех признаков прерываний MC_SWIC_STATUS(u->num) = SWIC_RESET_ALL_IRQS; // Сброс счетчиков принятых пакетов MC_SWIC_CNT_RX_PACK(u->num) = 0; MC_SWIC_CNT_RX0_PACK(u->num) = 0; } static void spw_init_dma (struct mc_spw *u) { PDEBUGG ("=== spw_init_dma: channel %d\n", u->num); // Очистка приемного буфера для того, чтобы правильно // работал поиск первого пакета после соединения memset (u->rxbuf, 0, RX_DATA_BUFSZ); // Сброс всех управляющих регистров DMA MC_SWIC_RX_DESC_CSR(u->num) = 0; MC_SWIC_RX_DATA_CSR(u->num) = 0; MC_SWIC_TX_DESC_CSR(u->num) = 0; MC_SWIC_TX_DATA_CSR(u->num) = 0; // Старт DMA приема данных MC_SWIC_RX_DATA_CP (u->num) = mips_virt_to_phys ((unsigned) &u->rx_data_chain) | 1; // Старт DMA приема дескрипторов MC_SWIC_RX_DESC_CP (u->num) = mips_virt_to_phys ((unsigned) u->rx_desc_chain) | 1; } static void spw_clean_dma (struct mc_spw *u) { uint32_t mode; PDEBUG ("+++ spw_clean_dma: channel %d\n", u->num); disable_irq (spw_rx_desc_irq (u->num)); disable_irq (spw_tx_data_irq (u->num)); mode = MC_SWIC_MODE_CR (u->num); mode &= ~ (MC_SWIC_AutoStart | MC_SWIC_LinkStart); mode |= MC_SWIC_LinkDisabled; MC_SWIC_MODE_CR (u->num) = mode; MC_SWIC_RX_DESC_RUN (u->num) = 0; MC_SWIC_RX_DATA_RUN (u->num) = 0; MC_SWIC_TX_DESC_RUN (u->num) = 0; MC_SWIC_TX_DATA_RUN (u->num) = 0; mode |= MC_SWIC_SWCORE_RST; MC_SWIC_MODE_CR (u->num) = mode; while ((MC_SWIC_RX_DESC_RUN (u->num) | MC_SWIC_RX_DATA_RUN (u->num) | MC_SWIC_TX_DESC_RUN (u->num) | MC_SWIC_TX_DATA_RUN (u->num)) & 1) { PDEBUG ("RX_DESC = %08X, RX_DATA = %08X, TX_DESC = %08X, TX_DATA = %08X, MODE_CR = %08X\n", MC_SWIC_RX_DESC_RUN (u->num), MC_SWIC_RX_DATA_RUN (u->num), MC_SWIC_TX_DESC_RUN (u->num), MC_SWIC_TX_DATA_RUN (u->num), MC_SWIC_MODE_CR (u->num)); } MC_SWIC_RX_DESC_CSR (u->num); MC_SWIC_RX_DATA_CSR (u->num); MC_SWIC_TX_DESC_CSR (u->num); MC_SWIC_TX_DATA_CSR (u->num); mode &= ~MC_SWIC_SWCORE_RST; MC_SWIC_MODE_CR (u->num) = mode; MC_SWIC_RX_DESC_CP (u->num) = 0; MC_SWIC_RX_DATA_CP (u->num) = 0; MC_SWIC_TX_DESC_CP (u->num) = 0; MC_SWIC_TX_DATA_CP (u->num) = 0; MC_SWIC_RX_DESC_IR (u->num) = 0; MC_SWIC_RX_DATA_IR (u->num) = 0; MC_SWIC_TX_DESC_IR (u->num) = 0; MC_SWIC_TX_DATA_IR (u->num) = 0; MC_SWIC_RX_DESC_CSR (u->num) = 0; MC_SWIC_RX_DATA_CSR (u->num) = 0; MC_SWIC_TX_DESC_CSR (u->num) = 0; MC_SWIC_TX_DATA_CSR (u->num) = 0; enable_irq (spw_rx_desc_irq (u->num)); enable_irq (spw_tx_data_irq (u->num)); PDEBUG (" spw_clean_dma done\n"); } static void spw_set_speed (struct mc_spw *u, unsigned speed) { u->speed = speed; MC_SWIC_TX_SPEED (u->num) = (MC_SWIC_TX_SPEED (u->num) & ~MC_SWIC_TX_SPEED_PRM_MASK) | MC_SWIC_TX_SPEED_PRM (speed / 5); } static void spw_stop (struct mc_spw *u) { PDEBUGG ("=== spw_stop: channel %d\n", u->num); MC_SWIC_RX_DATA_RUN (u->num) = 0; MC_SWIC_RX_DESC_RUN (u->num) = 0; MC_SWIC_TX_DATA_RUN (u->num) = 0; MC_SWIC_TX_DESC_RUN (u->num) = 0; MC_SWIC_MODE_CR (u->num) = MC_SWIC_LinkDisabled | SWIC_RESET; } /* * Setup MAC address */ static int spw_set_mac_addr (struct net_device *dev, void *addr) { PDEBUG ("=== 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) { PDEBUG ("=== spw_change_mtu\n"); if (new_mtu > ETH_MTU) return -EMSGSIZE; dev->mtu = new_mtu; return 0; } static inline int size_to_end_of_data_buffer (struct mc_spw *u) { return (u->rx_data_buffer + RX_DATA_BUFSZ - u->cur_rx_data); } static void move_rx_pointers (struct mc_spw *u, unsigned spare) { unsigned data_offset; PDEBUGG ("=== move_rx_pointers, channel = %d\n", u->num); // Циклический перенос указателя data_offset = u->cur_rx_data - u->rx_data_buffer; data_offset += (u->cur_rx_desc->size + 7) & ~7; data_offset %= RX_DATA_BUFSZ; *(u->cur_rx_data) = 0; u->cur_rx_data = u->rx_data_buffer + data_offset - spare; u->cur_rx_desc->valid = 0; if ( ++u->cur_rx_desc - u->rx_desc_buffer >= (RX_DESC_BUFSZ / sizeof (spw_descriptor_t))) { u->cur_rx_desc = u->rx_desc_buffer; } PDEBUGG ("=== move_rx_pointers done, channel = %d\n", u->num); } static inline void move_cpointer (struct mc_spw *u, unsigned char **pp) { *pp = *pp + 1; if (*pp >= (unsigned char *) u->rx_data_buffer + RX_DATA_BUFSZ) *pp = (unsigned char *) u->rx_data_buffer; } /* * Process a receive interrupt. */ static irqreturn_t spw_dma_rx_desc_ih (int irq, void *dev_id) { struct net_device *dev = dev_id; struct mc_spw *u = netdev_priv (dev); struct sk_buff *skb; unsigned char *data; unsigned sz_to_end, rest_pkt_size; unsigned nbytes; unsigned moved_len; unsigned char *paddr; int i; int broadcast; unsigned spare = 0; //static int prev_bad = 0; PDEBUGG ("=== spw_dma_rx_desc_ih, channel = %d, STATUS = %08X\n", u->num, MC_SWIC_STATUS (u->num)); PDEBUGG ("MC_SWIC_RX_DESC_CSR = %08X, MC_SWIC_RX_DESC_IR = %08X, QSTR2 = %08X\n", MC_SWIC_RX_DESC_CSR (u->num), MC_SWIC_RX_DESC_IR (u->num), MC_QSTR2); /* if (! (MC_SWIC_STATUS (u->num) & MC_SWIC_CONNECTED) ) { netif_stop_queue(dev); if (! timer_pending (&u->start_timer)) { u->no_restart = 0; u->start_timer.expires = jiffies + 1; PDEBUG ("start_timer.function = %08X, data = %08X\n", u->start_timer.function, u->start_timer.data); add_timer (&u->start_timer); } return IRQ_HANDLED; } */ // Сброс признака прерывания MC_SWIC_RX_DESC_CSR (u->num); PDEBUG ("dev_addr: %02x-%02x-%02x-%02x-%02x-%02x\n", dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); paddr = (unsigned char *) u->rx_data_buffer; PDEBUG ("rx_buf: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n", paddr[0], paddr[1], paddr[2], paddr[3], paddr[4], paddr[5], paddr[6], paddr[7], paddr[8], paddr[9], paddr[10], paddr[11], paddr[12], paddr[13], paddr[14], paddr[15]); for (; u->cur_rx_desc->valid; move_rx_pointers (u, spare)) { moved_len = 0; paddr = (unsigned char *) u->cur_rx_data; while ((*paddr != dev->dev_addr[0]) && (*paddr != 0xff)) { PDEBUG ("!!! MOVING POINTER\n"); while ((*paddr != dev->dev_addr[0]) && (*paddr != 0xff)) { move_cpointer (u, &paddr); ++moved_len; if (moved_len > RX_DATA_BUFSZ) { PDEBUG (" No valid packet!\n"); return IRQ_HANDLED; // Не нашлось нормального пакета } } broadcast = (*paddr == 0xff) ? (1) : (0); for (i = 1; i < dev->addr_len; ++i) if (broadcast) { if (paddr [i] != 0xff) break; } else { if (paddr [i] != dev->dev_addr [i]) break; } if (i == dev->addr_len) { break; } else { move_cpointer (u, &paddr); ++moved_len; } } u->cur_rx_data = (char *) paddr; PDEBUG ("paddr = %08X, dev->addr_len = %d\n", paddr, dev->addr_len); spare = ((unsigned) paddr) - ((unsigned) paddr & ~7); PDEBUG (" spw_dma_rx_desc_ih, spare = %d\n", spare); // Если пакет EEP, выкидываем его if (u->cur_rx_desc->type != EOP) { ++dev->stats.rx_errors; continue; } // Если пакет больше допустимого, выкидываем его if (u->cur_rx_desc->size > dev->mtu + ETH_HEADER_SIZE + ETH_CRC_SIZE) { ++dev->stats.rx_errors; continue; } /* Packet size is (len + 2) aligned to 4 bytes bound. * "+2" because we align IP header to 32 bits (see skb_reserve() below) */ skb = dev_alloc_skb ((u->cur_rx_desc->size + 2 + 3) & ~3); if (unlikely(skb == NULL)) { printk(KERN_NOTICE "%s: Low memory, packet dropped.\n", dev->name); dev->stats.rx_dropped++; continue; } /* Align IP header to 32 bits */ /* When using MEM_CH to copy from rx buffer this alignment is impossible, because the whole packet become unaligned to 32 bits and MEM_CH cannot copy to unaligned addresses */ skb_reserve (skb, 2); nbytes = u->cur_rx_desc->size; data = skb_put (skb, nbytes); sz_to_end = size_to_end_of_data_buffer (u); if (nbytes > sz_to_end) nbytes = sz_to_end; PDEBUG (" spw_dma_rx_desc_ih, len = %d, sz_to_end = %d, nbytes = %d\n", u->cur_rx_desc->size, sz_to_end, nbytes); memcpy (data, u->cur_rx_data, nbytes); rest_pkt_size = u->cur_rx_desc->size - nbytes; if (rest_pkt_size > 0) { memcpy (data + nbytes, u->rx_data_buffer, rest_pkt_size); } skb->protocol = eth_type_trans (skb, dev); netif_rx (skb); dev->stats.rx_packets++; dev->stats.rx_bytes += u->cur_rx_desc->size; } return IRQ_HANDLED; } static void spw_transmit_packet (struct sk_buff *skb, struct net_device *dev) { struct mc_spw *u = netdev_priv(dev); unsigned aligned_addr = ((unsigned) skb->data) & ~7; unsigned spare = ((unsigned) skb->data) - aligned_addr; unsigned char *paddr = (unsigned char *) aligned_addr; PDEBUG (" spw_transmit_packet, spare = %d\n", spare); PDEBUG ("tx_buf: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n", paddr[0], paddr[1], paddr[2], paddr[3], paddr[4], paddr[5], paddr[6], paddr[7], paddr[8], paddr[9], paddr[10], paddr[11], paddr[12], paddr[13], paddr[14], paddr[15]); //memcpy(u->tx_data_buffer, skb->data, skb->len); u->tx_desc.size = skb->len + spare; u->tx_desc.type = EOP; u->tx_desc.valid = 1; PDEBUGG (" tx_desc = %08X @ %08X\n", *( (unsigned *) &u->tx_desc ), (unsigned) &u->tx_desc ); MC_SWIC_TX_DESC_IR(u->num) = mips_virt_to_phys ((unsigned) &u->tx_desc); // Адрес начала буфера MC_SWIC_TX_DATA_IR(u->num) = mips_virt_to_phys ((unsigned) aligned_addr); MC_SWIC_TX_DESC_CSR(u->num) = MC_DMA_CSR_WCX(0) | MC_DMA_RUN; // 1 8-байтовое слово MC_SWIC_TX_DATA_CSR(u->num) = MC_DMA_CSR_WCX(((skb->len + spare + 7) >> 3) - 1) | MC_DMA_RUN /*| MC_DMA_CSR_IPD*/; //MC_SWIC_TX_DATA_CSR(u->num) = MC_DMA_CSR_WCX(0x1ff) | MC_DMA_RUN; dev->trans_start = jiffies; dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; } /* * Process a transmit interrupt */ static irqreturn_t spw_dma_tx_data_ih (int irq, void *dev_id) { struct net_device *dev = dev_id; struct mc_spw *u = netdev_priv(dev); PDEBUG ("=== spw_dma_tx_data_ih, channel = %d\n", u->num); PDEBUGG ("MC_SWIC_TX_DATA_CSR = %08X, QSTR2 = %08X\n", MC_SWIC_TX_DATA_RUN (u->num), MC_QSTR2); //u->no_restart = 1; #ifdef RETRANS_ON if (MC_SWIC_STATUS (u->num) & 0xf) { MC_SWIC_STATUS (u->num) |= 0xf; PDEBUG ("spw_dma_tx_data_ih: retransmitting recent packet\n"); if (u->recent_tx_skb) { spw_transmit_packet (u->recent_tx_skb, dev); return IRQ_HANDLED; } } else { dev_kfree_skb (u->recent_tx_skb); u->recent_tx_skb = 0; netif_wake_queue (dev); } #endif if (MC_SWIC_TX_DATA_CSR (u->num) & 1) { /* Transmitter not yet finished */ PDEBUG ("spw_dma_tx_data_ih: tx not yet finished, DESC CSR = %08X, DATA CSR = %08X\n", MC_SWIC_TX_DESC_RUN (u->num), MC_SWIC_TX_DATA_RUN (u->num)); return IRQ_HANDLED; } if (u->pending_tx_skb) { PDEBUGG ("Transmitting pending skb in interrupt handler\n"); spw_transmit_packet (u->pending_tx_skb, dev); dev_kfree_skb (u->pending_tx_skb); netif_wake_queue (dev); u->pending_tx_skb = 0; } return IRQ_HANDLED; } static netdev_tx_t spw_start_xmit (struct sk_buff *skb, struct net_device *dev) { struct mc_spw *u = netdev_priv(dev); PDEBUG ("=== spw_start_xmit: channel %d transmit %d bytes\n", u->num, skb->len); 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)); return NETDEV_TX_OK; } static void spw_struct_init (struct mc_spw *u) { int i; // u->no_restart = 0; u->pending_tx_skb = 0; u->recent_tx_skb = 0; u->rx_desc_buffer = (spw_descriptor_t *) u->rxdescbuf; u->rx_data_buffer = u->rxbuf; u->cur_rx_desc = u->rx_desc_buffer; u->cur_rx_data = u->rx_data_buffer; u->tx_data_buffer = u->txbuf; // Инициализация цепочки RX DESC DMA. Цепочка закольцована, прерывание // формируется по приему каждого дескриптора for (i = 0; i < RX_DESC_BUFSZ / sizeof (spw_descriptor_t); ++i) { u->rx_desc_chain[i].ir = mips_virt_to_phys ((unsigned) &u->rx_desc_buffer[i]); u->rx_desc_chain[i].csr = MC_DMA_CSR_IM | MC_DMA_CSR_CHEN | MC_DMA_CSR_WN(0) | MC_DMA_CSR_WCX(0) | MC_DMA_CSR_RUN; u->rx_desc_chain[i].cp = mips_virt_to_phys ((unsigned) &u->rx_desc_chain[i + 1]); } u->rx_desc_chain[i - 1].cp = mips_virt_to_phys ((unsigned) u->rx_desc_chain); /* unsigned *p = (unsigned *) u->rx_desc_chain; PDEBUG ("u->rx_desc_chain @ %08X\n", u->rx_desc_chain); for (i = 0; i < RX_DESC_BUFSZ / sizeof (dma_params_t); ++i) { PDEBUG ("%08X, %08X, %08X, %08X\n", *p, *(p + 1), *(p + 2), *(p + 3)); p += 4; } */ // Инициализация цепочки RX DATA DMA. Цепочка состоит из всего одного // закольцованного блока, прерывания замаскированы. u->rx_data_chain.ir = mips_virt_to_phys ((unsigned) u->rx_data_buffer); u->rx_data_chain.csr = MC_DMA_CSR_CHEN | MC_DMA_CSR_WN(0) | MC_DMA_CSR_WCX((RX_DATA_BUFSZ >> 3) - 1) | MC_DMA_CSR_RUN; u->rx_data_chain.cp = mips_virt_to_phys ((unsigned) &u->rx_data_chain); memset (u->rx_desc_buffer, 0, RX_DESC_BUFSZ); u->speed = SPEED_AFTER_START; } static void connect_swic (unsigned long data) { struct mc_spw *u = (struct mc_spw *) data; // if (u->no_restart) return; if ((MC_SWIC_STATUS (u->num) & 0x30EF) == 0x30A0) { PDEBUG ("SWIC%d started!\n", u->num); spw_init_dma (u); spw_set_speed (u, u->speed); if (netif_queue_stopped(u->pdev)) netif_wake_queue(u->pdev); else netif_start_queue(u->pdev); PDEBUGG (" 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)); return; } else { u->start_timer.expires = jiffies + 250; add_timer (&u->start_timer); } } static irqreturn_t spw_connected_ih (int irq, void *dev_id) { struct net_device *dev = dev_id; struct mc_spw *u = netdev_priv(dev); PDEBUG ("=== spw_connected_ih, STATUS = %08X\n", MC_SWIC_STATUS(u->num)); MC_SWIC_STATUS (u->num) |= MC_SWIC_CONNECTED; PDEBUG ("SWIC%d started!\n", u->num); spw_init_dma (u); spw_set_speed (u, u->speed); // Закрываем прерывание, иначе оно будет возникать по приему каждого пакета MC_SWIC_MODE_CR(u->num) &= ~MC_SWIC_LINK_MASK; if (netif_queue_stopped(u->pdev)) netif_wake_queue(u->pdev); else netif_start_queue(u->pdev); u->just_connected = 1; return IRQ_HANDLED; } static int spw_open (struct net_device *dev) { struct mc_spw *u = netdev_priv (dev); int ret; PDEBUG ("=== spw_open channel %d\n", u->num); 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)); PDEBUG ("=== spw_open done!\n"); return 0; } static int spw_close (struct net_device *dev) { struct mc_spw *u = netdev_priv (dev); PDEBUG ("=== spw_close\n"); free_irq (spw_rx_desc_irq (u->num), dev); free_irq (spw_tx_data_irq (u->num), dev); spw_stop (u); if (!netif_queue_stopped(dev)) netif_stop_queue(dev); return 0; } static int spw_init (struct net_device *dev) { PDEBUG ("=== spw_init\n"); return 0; } static const struct net_device_ops netdev_ops = { .ndo_init = spw_init, .ndo_open = spw_open, .ndo_stop = spw_close, .ndo_start_xmit = spw_start_xmit, .ndo_change_mtu = spw_change_mtu, .ndo_set_mac_address = spw_set_mac_addr, }; // // Печать настроек через файловую систему /proc // static int read_proc (char *buf, char **start, off_t offset, int count, int *eof, void *data) { struct mc_spw *u = data; int len = 0; len += sprintf (buf + len, "TX Speed = %d Mbit/sec\n", (MC_SWIC_TX_SPEED (u->num) & MC_SWIC_TX_SPEED_PRM_MASK) * 5); len += sprintf (buf + len, "RX Speed = %d Mbit/sec\n", (MC_SWIC_RX_SPEED (u->num) * mips_hpt_frequency / 1000000) >> 7); *eof = 1; return len; } // // Настройка SWIC через файловую систему /proc // static int write_proc (struct file *file, const char __user *buffer, unsigned long count, void *data) { struct mc_spw *u = data; const int buf_max_size = 256; char buf [buf_max_size]; char *p; int buf_size = (count < buf_max_size) ? (count) : (buf_max_size); int speed; if (copy_from_user (buf, buffer, buf_size)) return (-EMSGSIZE); p = strstr (buf, "speed"); if (!p) p = strstr (buf, "Speed"); if (!p) p = strstr (buf, "SPEED"); if (!p) return (-EINVAL); p += 6; // Теперь p указывает на число - скорость, которую необходимо установить speed = simple_strtoul (p, &p, 10); if (speed < 5 || speed > 400) return (-EINVAL); spw_set_speed (u, speed); return buf_size; } struct timer_list tim; static int __init spw_module_init(void) { struct net_device *ndev; struct mc_spw *u; struct proc_dir_entry *proc_dir; char proc_filename [8]; int ret; int i; PDEBUG ("=== spw_module_init\n"); for (i = 0; i < NB_OF_CHANNELS; ++i) { ndev = alloc_etherdev (sizeof (struct mc_spw)); if (!ndev) { printk ("%s%d: failed to allocate device.\n", CARD_NAME, i); return -ENOMEM; } ndev->dma = (unsigned char)-1; ndev->irq = spw_rx_desc_irq (i); ndev->netdev_ops = &netdev_ops; 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); u = netdev_priv (ndev); u->pdev = ndev; u->num = i; init_timer (&u->start_timer); u->start_timer.function = connect_swic; u->start_timer.data = (unsigned) u; ret = register_netdev (ndev); if (ret) { printk ("%s%d: failed to register device.\n", CARD_NAME, i); free_netdev(ndev); } sprintf (proc_filename, "spw%d", 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; } } PDEBUG ("=== spw_module_init done!\n"); return 0; } static void __exit spw_module_cleanup(void) { } module_init(spw_module_init); module_exit(spw_module_cleanup);