| /** @filec |
| * @brief RL8168 Driver |
| * |
| * EXPERIMENTAL. DO NOT USE IF YOU DONT KNOW WHAT YOU ARE DOING |
| * |
| * See Info below |
| * |
| * @author Paul Pearce <pearce@eecs.berkeley.edu> |
| * |
| */ |
| |
| #ifdef __SHARC__ |
| #pragma nosharc |
| #endif |
| |
| #include <arch/mmu.h> |
| #include <arch/x86.h> |
| #include <arch/smp.h> |
| #include <arch/apic.h> |
| #include <arch/pci.h> |
| #include "rl8168.h" |
| |
| #include <ros/memlayout.h> |
| |
| #include <atomic.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <trap.h> |
| #include <kmalloc.h> |
| |
| #include <pmap.h> |
| |
| #include <eth_audio.h> |
| #include <net/pbuf.h> |
| |
| |
| /** @file |
| * @brief Realtek RL8168 Driver |
| * |
| * EXPERIMENTAL. DO NOT USE IF YOU DONT KNOW WHAT YOU ARE DOING |
| * |
| * This is a function rl8168 driver, that uses some really ugly hacks to achieve |
| * UDP communication with a remote syscall server, without a network stack. |
| * |
| * To enable use, define CONFIG_NETWORKING in your Makelocal |
| * |
| * @author Paul Pearce <pearce@eecs.berkeley.edu> |
| * |
| * @todo Move documention below into doxygen format. |
| * @todo See list in code |
| */ |
| |
| |
| /* RealTek 8168d (8111d) NIC Driver |
| * |
| * Written by Paul Pearce. |
| * |
| * This is a really rough "driver". Really, its not a driver, just a kernel hack to give |
| * the kernel a way to receive and send packets. The basis of the init code is the OSDEV |
| * page on the 8169 chipset, which is a varient of this chipset (most 8169 drivers work |
| * on the 8168d). http://wiki.osdev.org/RTL8169 |
| * |
| * Basic ideas (although no direct code) were gleamed from the OpenBSD re(4) driver, |
| * which can be found in sys/dev/ic/re.c. sys/dev/ic/rtl81x9reg.h is needed to make |
| * sense of the constants used in re.c. |
| * |
| * This is an ongoing work in progress. Main thing is we need a kernel interface for PCI |
| * devices and network devices, that we can hook into, instead of providing arbitary functions |
| * |
| * TODO: Remove hacky syscall hack stuff (once we get a real stack). |
| * TODO: Jumbo frame support |
| * TODO: Use high priority transmit ring for syscall stuff. |
| * TODO: Discuss panic conditions. |
| * TODO: Shutdown cleanup kfrees() |
| * TODO: Use onboard timer interrupt to check for packets, instead of writing a bit each time we have a packet. |
| * TODO: CONCURRENCY! |
| */ |
| |
| struct Descriptor |
| { |
| unsigned int command, /* command/status dword */ |
| vlan, /* currently unused */ |
| low_buf, /* low 32-bits of physical buffer address */ |
| high_buf; /* high 32-bits of physical buffer address */ |
| }; |
| |
| |
| static uint32_t rl8168_io_base_addr = 0; |
| static uint32_t rl8168_irq = 0; |
| |
| static struct Descriptor *CT(NUM_RX_DESCRIPTORS) rx_des_kva; |
| static unsigned long rx_des_pa; |
| |
| static struct Descriptor *CT(NUM_TX_DESCRIPTORS) tx_des_kva; |
| static unsigned long tx_des_pa; |
| |
| static uint32_t rx_des_cur = 0; |
| static uint32_t tx_des_cur = 0; |
| |
| |
| |
| void rl8168_init() { |
| |
| if (rl8168_scan_pci() < 0) return; |
| rl8168_read_mac(); |
| printk("Network Card MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n", |
| device_mac[0],device_mac[1],device_mac[2], |
| device_mac[3],device_mac[4],device_mac[5]); |
| rl8168_setup_descriptors(); |
| rl8168_configure(); |
| rl8168_setup_interrupts(); |
| send_frame = &rl8168_send_frame; |
| send_pbuf = &rl8168_send_pbuf; |
| |
| eth_up = 1; |
| |
| //Trigger sw based nic interrupt |
| /* cprintf("Generating interrupt...\n"); |
| outb(rl8168_io_base_addr + 0x38, 0x1); |
| cprintf("sleeping\n"); |
| udelay(3000000); |
| cprintf("done\n"); |
| */ |
| return; |
| } |
| |
| |
| int rl8168_scan_pci() { |
| struct pci_device *pcidev; |
| uint32_t result; |
| printk("Searching for RealTek 8168 Network device..."); |
| STAILQ_FOREACH(pcidev, &pci_devices, all_dev) { |
| /* Ignore non RealTek 8168 Devices */ |
| if ((pcidev->ven_id != REALTEK_VENDOR_ID) || |
| (pcidev->dev_id != REALTEK_DEV_ID)) |
| continue; |
| printk(" found on BUS %x DEV %x\n", pcidev->bus, pcidev->dev); |
| /* Find the IRQ */ |
| rl8168_irq = pcidev->irqline; |
| rl8168_debug("-->IRQ: %u\n", rl8168_irq); |
| /* Loop over the BARs */ |
| for (int k = 0; k <= 5; k++) { |
| int reg = 4 + k; |
| result = pcidev_read32(pcidev, reg << 2); // SHAME! |
| if (result == 0) // (0 denotes no valid data) |
| continue; |
| // Read the bottom bit of the BAR. |
| if (result & PCI_BAR_IO) { |
| result = result & PCI_BAR_IO_MASK; |
| rl8168_debug("-->BAR%u: %s --> %x\n", k, "IO", result); |
| } else { |
| result = result & PCI_BAR_MEM_MASK; |
| rl8168_debug("-->BAR%u: %s --> %x\n", k, "MEM", result); |
| } |
| // TODO Switch to memory mapped instead of IO? |
| if (k == 0) // BAR0 denotes the IO Addr for the device |
| rl8168_io_base_addr = result; |
| } |
| rl8168_debug("-->hwrev: %x\n", |
| inl(rl8168_io_base_addr + RL_HWREV_REG) & RL_HWREV_MASK); |
| return 0; |
| } |
| printk(" not found. No device configured.\n"); |
| return -1; |
| } |
| |
| void rl8168_read_mac() { |
| |
| for (int i = 0; i < 6; i++) |
| device_mac[i] = inb(rl8168_io_base_addr + RL_MAC_OFFSET + i); |
| |
| rl8168_debug("-->DEVICE MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", 0xFF & device_mac[0], 0xFF & device_mac[1], |
| 0xFF & device_mac[2], 0xFF & device_mac[3], |
| 0xFF & device_mac[4], 0xFF & device_mac[5]); |
| return; |
| } |
| |
| void rl8168_setup_descriptors() { |
| |
| rl8168_debug("-->Setting up tx/rx descriptors.\n"); |
| |
| // Allocate room for the buffers. |
| // Buffers need to be on 256 byte boundries. |
| // Note: We use get_cont_pages to force page alignment, and thus 256 byte aligned |
| uint32_t num_rx_pages = ROUNDUP(NUM_RX_DESCRIPTORS * sizeof(struct Descriptor), PGSIZE) / PGSIZE; |
| uint32_t num_tx_pages = ROUNDUP(NUM_TX_DESCRIPTORS * sizeof(struct Descriptor), PGSIZE) / PGSIZE; |
| |
| rx_des_kva = get_cont_pages(LOG2_UP(num_rx_pages), 0); |
| tx_des_kva = get_cont_pages(LOG2_UP(num_tx_pages), 0); |
| |
| if (rx_des_kva == NULL) panic("Can't allocate page for RX Ring"); |
| if (tx_des_kva == NULL) panic("Can't allocate page for TX Ring"); |
| |
| rx_des_pa = PADDR(rx_des_kva); |
| tx_des_pa = PADDR(tx_des_kva); |
| |
| for (int i = 0; i < NUM_RX_DESCRIPTORS; i++) |
| rl8168_set_rx_descriptor(i, TRUE); // Allocate memory for the descriptor |
| |
| for (int i = 0; i < NUM_TX_DESCRIPTORS; i++) |
| rl8168_set_tx_descriptor(i); |
| |
| return; |
| } |
| |
| |
| void rl8168_set_rx_descriptor(uint32_t des_num, uint8_t reset_buffer) { |
| |
| // Set the OWN bit on all descriptors. Also set the buffer size. |
| rx_des_kva[des_num].command = (DES_OWN_MASK | (RL_RX_MAX_BUFFER_SIZE & DES_RX_SIZE_MASK)); |
| |
| if (des_num == (NUM_RX_DESCRIPTORS - 1)) |
| rx_des_kva[des_num].command = rx_des_kva[des_num].command | DES_EOR_MASK; |
| |
| if (reset_buffer) { |
| // Must be aligned on 8 byte boundries. Taken care of by kmalloc. |
| char *rx_buffer = kmalloc(RL_RX_MAX_BUFFER_SIZE, 0); |
| |
| if (rx_buffer == NULL) panic ("Can't allocate page for RX Buffer"); |
| |
| rx_des_kva[des_num].low_buf = PADDR(rx_buffer); |
| //.high_buf used if we do 64bit. |
| } |
| |
| return; |
| } |
| |
| void rl8168_set_tx_descriptor(uint32_t des_num) { |
| |
| // Clear the command bits. |
| tx_des_kva[des_num].command = 0; |
| |
| // Set EOR bit on last descriptor |
| if (des_num == (NUM_TX_DESCRIPTORS - 1)) |
| tx_des_kva[des_num].command = DES_EOR_MASK; |
| |
| char *tx_buffer = kmalloc(RL_TX_MAX_BUFFER_SIZE, 0); |
| |
| if (tx_buffer == NULL) panic ("Can't allocate page for TX Buffer"); |
| |
| tx_des_kva[des_num].low_buf = PADDR(tx_buffer); |
| //.high_buf used if we do 64bit. |
| |
| return; |
| } |
| |
| void rl8168_configure() { |
| |
| // TODO: Weigh resetting the nic. Not really needed. Remove? |
| // TODO Check ordering of what we set. |
| // TODO Remove C+ register setting? |
| |
| rl8168_debug("-->Configuring Device.\n"); |
| rl8168_reset(); |
| |
| // Magic to handle the C+ register. Completely undocumented, ripped from the BSE RE driver. |
| outl(rl8168_io_base_addr + RL_CP_CTRL_REG, RL_CP_MAGIC_MASK); |
| |
| // Unlock EPPROM CTRL REG |
| outb(rl8168_io_base_addr + RL_EP_CTRL_REG, RL_EP_CTRL_UL_MASK); |
| |
| // Set max RX Packet Size |
| outw(rl8168_io_base_addr + RL_RX_MXPKT_REG, RL_RX_MAX_SIZE); |
| |
| // Set max TX Packet Size |
| outb(rl8168_io_base_addr + RL_TX_MXPKT_REG, RL_TX_MAX_SIZE); |
| |
| // Set TX Des Ring Start Addr |
| outl(rl8168_io_base_addr + RL_TX_DES_REG, (unsigned long)tx_des_pa); |
| |
| // Set RX Des Ring Start Addr |
| outl(rl8168_io_base_addr + RL_RX_DES_REG, (unsigned long)rx_des_pa); |
| |
| // Configure TX |
| outl(rl8168_io_base_addr + RL_TX_CFG_REG, RL_TX_CFG_MASK); |
| |
| // Configure RX |
| outl(rl8168_io_base_addr + RL_TX_CFG_REG, RL_RX_CFG_MASK); |
| |
| // Enable RX and TX in the CTRL Reg |
| outb(rl8168_io_base_addr + RL_CTRL_REG, RL_CTRL_RXTX_MASK); |
| |
| // Lock the EPPROM Ctrl REG |
| outl(rl8168_io_base_addr + RL_EP_CTRL_REG, RL_EP_CTRL_L_MASK); |
| |
| return; |
| } |
| |
| void rl8168_reset() { |
| |
| rl8168_debug("-->Resetting device..... "); |
| outb(rl8168_io_base_addr + RL_CTRL_REG, RL_CTRL_RESET_MASK); |
| |
| // Wait for NIC to answer "done resetting" before continuing on |
| while (inb(rl8168_io_base_addr + RL_CTRL_REG) & RL_CTRL_RESET_MASK); |
| rl8168_debug(" done.\n"); |
| |
| return; |
| } |
| |
| void rl8168_setup_interrupts() { |
| |
| rl8168_debug("-->Setting interrupts.\n"); |
| |
| // Enable NIC interrupts |
| outw(rl8168_io_base_addr + RL_IM_REG, RL_INTERRUPT_MASK); |
| |
| //Clear the current interrupts. |
| outw(rl8168_io_base_addr + RL_IS_REG, RL_INTRRUPT_CLEAR); |
| |
| // Kernel based interrupt stuff |
| register_dev_irq(rl8168_irq, rl8168_interrupt_handler, 0); |
| } |
| |
| // We need to evaluate this routine in terms of concurrency. |
| // We also need to figure out whats up with different core interrupts |
| void rl8168_interrupt_handler(struct hw_trapframe *hw_tf, void *data) |
| { |
| |
| rl8168_interrupt_debug("\nNic interrupt on core %u!\n", lapic_get_id()); |
| |
| // Read the offending interrupt(s) |
| uint16_t interrupt_status = inw(rl8168_io_base_addr + RL_IS_REG); |
| |
| // Clear interrupts immediately so we can get the flag raised again. |
| outw(rl8168_io_base_addr + RL_IS_REG, interrupt_status); |
| |
| // Loop to deal with TOCTOU |
| while (interrupt_status != 0x0000) { |
| // We can have multiple interrupts fire at once. I've personally seen this. |
| // This means we need to handle this as a series of independent if's |
| if (interrupt_status & RL_INT_ROK) { |
| rl8168_interrupt_debug("-->RX OK\n"); |
| rl8168_handle_rx_packet(); |
| } |
| |
| if (interrupt_status & RL_INT_RERR) { |
| rl8168_interrupt_debug("-->RX ERR\n"); |
| } |
| |
| if (interrupt_status & RL_INT_TOK) { |
| rl8168_interrupt_debug("-->TX OK\n"); |
| } |
| |
| if (interrupt_status & RL_INT_TERR) { |
| rl8168_interrupt_debug("-->TX ERR\n"); |
| } |
| |
| if (interrupt_status & RL_INT_RDU) { |
| rl8168_interrupt_debug("-->RX Descriptor Unavailable\n"); |
| } |
| |
| if (interrupt_status & RL_INT_LINKCHG) { |
| rl8168_interrupt_debug("-->Link Status Changed\n"); |
| } |
| |
| if (interrupt_status & RL_INT_FOVW) { |
| rl8168_interrupt_debug("-->RX Fifo Overflow\n"); |
| } |
| |
| if (interrupt_status & RL_INT_TDU) { |
| rl8168_interrupt_debug("-->TX Descriptor Unavailable\n"); |
| } |
| |
| if (interrupt_status & RL_INT_SWINT) { |
| rl8168_interrupt_debug("-->Software Generated Interrupt\n"); |
| } |
| |
| if (interrupt_status & RL_INT_TIMEOUT) { |
| rl8168_interrupt_debug("-->Timer Expired\n"); |
| } |
| |
| if (interrupt_status & RL_INT_SERR) { |
| rl8168_interrupt_debug("-->PCI Bus System Error\n"); |
| } |
| |
| rl8168_interrupt_debug("\n"); |
| |
| // Clear interrupts |
| interrupt_status = inw(rl8168_io_base_addr + RL_IS_REG); |
| outw(rl8168_io_base_addr + RL_IS_REG, interrupt_status); |
| } |
| |
| // In the event that we got really unlucky and more data arrived after we set |
| // set the bit last, try one more check |
| rl8168_handle_rx_packet(); |
| |
| return; |
| } |
| |
| // TODO: Does a packet too large get dropped or just set the error bits in the descriptor? Find out. |
| // TODO: Should we move on to look for the next descriptor? is it safe? TOCTOU |
| void rl8168_handle_rx_packet() { |
| |
| uint32_t current_command = rx_des_kva[rx_des_cur].command; |
| uint16_t packet_size; |
| |
| if (current_command & DES_OWN_MASK) { |
| rl8168_frame_debug("-->Nothing to process. Returning."); |
| return; |
| } |
| |
| rl8168_frame_debug("-->RX Des: %u\n", rx_des_cur); |
| |
| // Make sure we are processing from the start of a packet segment |
| if (!(current_command & DES_FS_MASK)) { |
| rl8168_frame_debug("-->ERR: Current RX descriptor not marked with FS mask. Panic!"); |
| panic("RX Descriptor Ring FS out of sync"); |
| } |
| |
| // NOTE: We are currently configured that the max packet size is large enough to fit inside 1 descriptor buffer, |
| // So we should never be in a situation where a packet spans multiple descriptors. |
| // When we change this, this should operate in a loop until the LS mask is found |
| // Loop would begin here. |
| |
| uint32_t rx_des_loop_cur = rx_des_cur; |
| uint32_t frame_size = 0; |
| uint32_t fragment_size = 0; |
| uint32_t num_frags = 0; |
| |
| char *rx_buffer = kmalloc(MAX_FRAME_SIZE, 0); |
| |
| if (rx_buffer == NULL) panic ("Can't allocate page for incoming packet."); |
| |
| do { |
| current_command = rx_des_kva[rx_des_loop_cur].command; |
| fragment_size = rx_des_kva[rx_des_loop_cur].command & DES_RX_SIZE_MASK; |
| |
| // If we've looped through the entire ring and not found a terminating packet, bad nic state. |
| // Panic or clear all descriptors? This is a nic hardware error. |
| if (num_frags && (rx_des_loop_cur == rx_des_cur)) { |
| //for (int i = 0; i < NUM_RX_DESCRIPTORS; i++) |
| // set_rx_descriptor(i, FALSE); // Dont reallocate memory for the descriptor |
| // rx_des_cur = 0; |
| // return; |
| rl8168_frame_debug("-->ERR: No ending segment found in RX buffer.\n"); |
| panic("RX Descriptor Ring out of sync."); |
| } |
| |
| num_frags++; |
| |
| |
| // Make sure we own the current packet. Kernel ownership is denoted by a 0. Nic by a 1. |
| if (current_command & DES_OWN_MASK) { |
| rl8168_frame_debug("-->ERR: Current RX descriptor not owned by kernel. Panic!"); |
| panic("RX Descriptor Ring OWN out of sync"); |
| } |
| |
| // Make sure if we are at the end of the buffer, the des is marked as end |
| if ((rx_des_loop_cur == (NUM_RX_DESCRIPTORS - 1)) && !(current_command & DES_EOR_MASK)) { |
| rl8168_frame_debug("-->ERR: Last RX descriptor not marked with EOR mask. Panic!\n"); |
| panic("RX Descriptor Ring EOR Missing"); |
| } |
| |
| // We set a max frame size and the nic violated that. |
| // Panic or clear all desriptors? |
| if ((frame_size + fragment_size) > MAX_FRAME_SIZE) { |
| //for (int i = 0; i < NUM_RX_DESCRIPTORS; i++) |
| // set_rx_descriptor(i, FALSE); // Dont reallocate memory for the descriptor |
| // rx_des_cur = 0; |
| // return; |
| rl8168_frame_debug("-->ERR: Nic sent %u byte packet. Max is %u\n", frame_size, MAX_FRAME_SIZE); |
| panic("NIC Sent packets larger than configured."); |
| } |
| |
| // Move the fragment data into the buffer |
| memcpy(rx_buffer + frame_size, KADDR(rx_des_kva[rx_des_loop_cur].low_buf), fragment_size); |
| |
| // Reset the descriptor. No reuse buffer. |
| rl8168_set_rx_descriptor(rx_des_loop_cur, FALSE); |
| |
| // Note: We mask out fragment sizes at 0x3FFFF. There can be at most 1024 of them. |
| // This can not overflow the uint32_t we allocated for frame size, so |
| // we dont need to worry about mallocing too little then overflowing when we read. |
| frame_size = frame_size + fragment_size; |
| |
| // Advance to the next descriptor |
| rx_des_loop_cur = (rx_des_loop_cur + 1) % NUM_RX_DESCRIPTORS; |
| |
| } while (!(current_command & DES_LS_MASK)); |
| |
| #ifdef CONFIG_APPSERVER |
| // Treat as a syscall frontend response packet if eth_type says so |
| // Will eventually go away, so not too worried about elegance here... |
| #include <frontend.h> |
| #include <arch/frontend.h> |
| uint16_t eth_type = htons(*(uint16_t*)(rx_buffer + 12)); |
| if(eth_type == APPSERVER_ETH_TYPE) { |
| rx_des_cur = rx_des_loop_cur; |
| rl8168_process_frame(rx_buffer, frame_size, current_command); |
| handle_appserver_packet(rx_buffer, frame_size); |
| kfree(rx_buffer); |
| return; |
| } |
| #endif |
| #ifdef CONFIG_ETH_AUDIO |
| /* TODO: move this, and all packet processing, out of this driver (including |
| * the ghetto buffer). Note we don't handle IP fragment reassembly (though |
| * this isn't an issue for the eth_audio). */ |
| struct ethaud_udp_packet *packet = (struct ethaud_udp_packet*)rx_buffer; |
| uint8_t protocol = packet->ip_hdr.protocol; |
| uint16_t udp_port = ntohs(packet->udp_hdr.dst_port); |
| if (protocol == IPPROTO_UDP && udp_port == ETH_AUDIO_RCV_PORT) { |
| rx_des_cur = rx_des_loop_cur; |
| eth_audio_newpacket(packet); |
| kfree(rx_buffer); |
| return; |
| } |
| #endif /* CONFIG_ETH_AUDIO */ |
| |
| spin_lock(&packet_buffers_lock); |
| |
| if (num_packet_buffers >= MAX_PACKET_BUFFERS) { |
| //printk("WARNING: DROPPING PACKET!\n"); |
| spin_unlock(&packet_buffers_lock); |
| rx_des_cur = rx_des_loop_cur; |
| kfree(rx_buffer); |
| return; |
| } |
| |
| packet_buffers[packet_buffers_tail] = rx_buffer; |
| packet_buffers_sizes[packet_buffers_tail] = frame_size; |
| |
| packet_buffers_tail = (packet_buffers_tail + 1) % MAX_PACKET_BUFFERS; |
| num_packet_buffers++; |
| |
| spin_unlock(&packet_buffers_lock); |
| |
| rx_des_cur = rx_des_loop_cur; |
| |
| // Chew on the frame data. Command bits should be the same for all frags. |
| rl8168_process_frame(rx_buffer, frame_size, current_command); |
| |
| return; |
| } |
| |
| // This is really more of a debug level function. Will probably go away once we get a stack going. |
| void rl8168_process_frame(char *frame_buffer, uint32_t frame_size, uint32_t command) { |
| |
| rl8168_frame_debug("-->Command: %x\n", command); |
| rl8168_frame_debug("-->Size: %u\n", frame_size); |
| |
| if (frame_buffer == NULL) |
| return; |
| |
| // This is hacky. Once we know what our stack will look like, change this. |
| // Once remove check for 0 size. |
| if (frame_size < MIN_FRAME_SIZE) { |
| rl8168_frame_debug("-->Packet too small. Discarding.\n"); |
| return; |
| } |
| |
| char dest_mac[6]; |
| char source_mac[6]; |
| char eth_type[2]; |
| |
| for (int i = 0; i < 6; i++) { |
| dest_mac[i] = frame_buffer[i]; |
| } |
| |
| for (int i = 0; i < 6; i++) { |
| source_mac[i] = frame_buffer[i+6]; |
| } |
| |
| eth_type[0] = frame_buffer[12]; |
| eth_type[1] = frame_buffer[13]; |
| |
| if (command & DES_MAR_MASK) { |
| rl8168_frame_debug("-->Multicast Packet.\n"); |
| } |
| |
| if (command & DES_PAM_MASK) { |
| rl8168_frame_debug("-->Physical Address Matched.\n"); |
| } |
| |
| if (command & DES_BAR_MASK) { |
| rl8168_frame_debug("-->Broadcast Packet.\n"); |
| } |
| |
| // Note: DEST comes before SRC in the ethernet frame, but that |
| |
| rl8168_frame_debug("-->DEST MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", 0xFF & dest_mac[0], 0xFF & dest_mac[1], |
| 0xFF & dest_mac[2], 0xFF & dest_mac[3], |
| 0xFF & dest_mac[4], 0xFF & dest_mac[5]); |
| |
| rl8168_frame_debug("-->SOURCE MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", 0xFF & source_mac[0], 0xFF & source_mac[1], |
| 0xFF & source_mac[2], 0xFF & source_mac[3], |
| 0xFF & source_mac[4], 0xFF & source_mac[5]); |
| |
| rl8168_frame_debug("-->ETHR MODE: %02x%02x\n", 0xFF & eth_type[0], 0xFF & eth_type[1]); |
| |
| return; |
| } |
| |
| /* Look into how bsd does send mbuf ? */ |
| int rl8168_send_pbuf(struct pbuf *p) { |
| int len = p->tot_len; |
| if (p == NULL) |
| return -1; |
| if (len == 0) |
| return 0; |
| |
| if (tx_des_kva[tx_des_cur].command & DES_OWN_MASK) { |
| rl8168_frame_debug("-->TX Ring Buffer Full!\n"); |
| return -1; |
| } |
| if (len > MAX_FRAME_SIZE){ |
| return -1; |
| } |
| /* Copy everything out of pbuf to network buffer to be sent */ |
| /* One copy! */ |
| pbuf_copy_out(p, KADDR(tx_des_kva[tx_des_cur].low_buf), len, 0); |
| tx_des_kva[tx_des_cur].command = tx_des_kva[tx_des_cur].command | len | DES_OWN_MASK | DES_FS_MASK | DES_LS_MASK; |
| |
| tx_des_kva[tx_des_cur].vlan = 0; |
| |
| |
| tx_des_cur = (tx_des_cur + 1) % NUM_TX_DESCRIPTORS; |
| |
| rl8168_frame_debug("--> Sending Packet\n"); |
| for(int i=0; i<len; i++) |
| rl8168_frame_debug("%x ", (unsigned int)(unsigned char)(data[i])); |
| rl8168_frame_debug("\n"); |
| rl8168_frame_debug("--> Sent packet.\n"); |
| |
| outb(rl8168_io_base_addr + RL_TX_CTRL_REG, RL_TX_SEND_MASK); |
| |
| return len; |
| } |
| // Main routine to send a frame. Just sends it and goes. |
| // Card supports sending across multiple fragments. |
| // Would we want to write a function that takes a larger packet and generates fragments? |
| // This seems like the stacks responsibility. Leave this for now. may in future |
| // Remove the max size cap and generate multiple packets. |
| int rl8168_send_frame(const char *data, size_t len) { |
| |
| if (data == NULL) |
| return -1; |
| if (len == 0) |
| return 0; |
| |
| if (tx_des_kva[tx_des_cur].command & DES_OWN_MASK) { |
| rl8168_frame_debug("-->TX Ring Buffer Full!\n"); |
| return -1; |
| } |
| |
| if (len > MAX_FRAME_SIZE) { |
| rl8168_frame_debug("-->Frame Too Large!\n"); |
| return -1; |
| } |
| |
| memcpy(KADDR(tx_des_kva[tx_des_cur].low_buf), data, len); |
| |
| tx_des_kva[tx_des_cur].command = tx_des_kva[tx_des_cur].command | len | DES_OWN_MASK | DES_FS_MASK | DES_LS_MASK; |
| |
| // For this revision of the NIC, the checksum bits get set in the vlan field not the command field. |
| // THIS IS A HACK: Need to reach inside the frame we are sending and detect if its of type ip/udp/tcp and set right flag |
| // For now, for the syscall hack, force ip checksum on. (we dont care about udp checksum). |
| // Add an argument to function to specify packet type? |
| //tx_des_kva[tx_des_cur].vlan = DES_TX_IP_CHK_MASK; |
| tx_des_kva[tx_des_cur].vlan = 0; |
| |
| |
| tx_des_cur = (tx_des_cur + 1) % NUM_TX_DESCRIPTORS; |
| |
| rl8168_frame_debug("--> Sending Packet\n"); |
| for(int i=0; i<len; i++) |
| rl8168_frame_debug("%x ", (unsigned int)(unsigned char)(data[i])); |
| rl8168_frame_debug("\n"); |
| rl8168_frame_debug("--> Sent packet.\n"); |
| |
| outb(rl8168_io_base_addr + RL_TX_CTRL_REG, RL_TX_SEND_MASK); |
| |
| return len; |
| } |
| |