2 * Acess2 VirtIO Network Driver
3 * - By John Hodge (thePowersGang)
9 #define VERSION VER2(1,0)
12 #include <semaphore.h>
14 #include <IPStack/include/adapters_api.h>
15 #include "virtio-net.h"
16 #include <Libraries/VirtIO/include/virtio.h>
21 typedef struct sVirtIONet_Dev tVirtIONet_Dev;
27 tSemaphore RXPacketSem;
28 void *RXBuffers[NRXBUFS];
32 int VirtIONet_Install(char **Arguments);
33 int VirtIONet_Cleanup(void);
34 void VirtIONet_AddCard(Uint16 IOBase, Uint IRQ);
35 int VirtIONet_RXQueueCallback(tVirtIO_Dev *Dev, int ID, size_t UsedBytes, void *Handle);
36 tIPStackBuffer *VirtIONet_WaitForPacket(void *Ptr);
37 int VirtIONet_TXQueueCallback(tVirtIO_Dev *Dev, int ID, size_t UsedBytes, void *Handle);
38 int VirtIONet_SendPacket(void *Ptr, tIPStackBuffer *Buffer);
41 MODULE_DEFINE(0, VERSION, VirtIONet, VirtIONet_Install, VirtIONet_Cleanup, "IPStack", "VirtIOCommon", NULL);
42 tIPStack_AdapterType gVirtIONet_AdapterType = {
44 .Type = ADAPTERTYPE_ETHERNET_1G, // TODO: Differentiate differnet wire protos and speeds
45 .Flags = ADAPTERFLAG_OFFLOAD_MAC, // TODO: IP/TCP/UDP checksum offloading
46 .SendPacket = VirtIONet_SendPacket,
47 .WaitForPacket = VirtIONet_WaitForPacket
51 int VirtIONet_Install(char **Arguments)
54 // Find network devices
55 while( (pcidev = PCI_GetDeviceByClass(0x020000, 0xFF0000, pcidev)) != -1 )
58 PCI_GetDeviceInfo(pcidev, &ven, &dev, NULL);
59 LOG("Device %i: %x/%x", pcidev, ven, dev);
60 // 0x1AF4:(0x1000-0x103F) are VirtIO devices
61 if( ven != 0x1AF4 || (dev & 0xFFC0) != 0x1000 )
64 PCI_GetDeviceSubsys(pcidev, &ven, &subsys_id);
65 if( subsys_id != 1 ) {
66 Log_Notice("VirtIONet", "Device with network PCI class but not subsys (%i!=1)", subsys_id);
70 Uint8 irq = PCI_GetIRQ(pcidev);
71 // TODO: Detect bad IRQ
73 Uint32 iobase = PCI_GetBAR(pcidev, 0);
78 VirtIONet_AddCard(iobase & ~1, irq);
83 int VirtIONet_Cleanup(void)
85 Log_Warning("VirtIONet", "TODO: Clean up before module unload");
89 void VirtIONet_AddCard(Uint16 IOBase, Uint IRQ)
91 ENTER("xMMIOBase xIRQ", IOBase, IRQ);
92 // Should be a VirtIO Network device
93 tVirtIO_Dev *dev = VirtIO_InitDev(
95 VIRTIO_NET_F_MAC|VIRTIO_NET_F_STATUS
97 |VIRTIO_NET_F_MRG_RXBUF
98 |VIRTIO_F_NOTIFY_ON_EMPTY,
100 sizeof(struct sVirtIONet_Dev)
105 tVirtIONet_Dev *ndev = VirtIO_GetDataPtr(dev);
106 Semaphore_Init(&ndev->RXPacketSem, 0, NRXBUFS, "VirtIONet", "RXSem");
107 ndev->Features = VirtIO_GetFeatures(dev);
110 if( ndev->Features & VIRTIO_NET_F_MAC ) {
111 for( int i = 0; i < 6; i ++ )
112 mac[i] = VirtIO_GetDevConfig(dev, 8, i);
113 Log_Log("VirtIONet", "Card %x,%i - MAC %02x:%02x:%02x:%02x:%02x:%02x",
115 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
118 // x[2,6,A,E] xx xx xx xx xx
122 mac[3] = rand() & 0xFF;
123 mac[4] = rand() & 0xFF;
124 mac[5] = rand() & 0xFF;
125 Log_Log("VirtIONet", "Card %x,%i - Random MAC %02x:%02x:%02x:%02x:%02x:%02x",
127 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
130 // NoAutoRel=1 : Keep RX buffers about until worker is done with them
131 VirtIO_SetQueueCallback(dev, 0, VirtIONet_RXQueueCallback, 1);
132 VirtIO_SetQueueCallback(dev, 1, VirtIONet_TXQueueCallback, 0);
135 for( int i = 0; i < NRXBUFS; i ++ )
137 ndev->RXBuffers[i] = MM_AllocDMA(1, -1, NULL);
138 LOG("RxBuf %i/%i: %p", i, NRXBUFS, ndev->RXBuffers[i]);
139 VirtIO_ReceiveBuffer(dev, 0, PAGE_SIZE, ndev->RXBuffers[i], NULL);
142 // Register with IPStack
144 // TODO: Save the returned pointer to do deregister later
145 IPStack_Adapter_Add(&gVirtIONet_AdapterType, dev, mac);
149 int VirtIONet_RXQueueCallback(tVirtIO_Dev *Dev, int ID, size_t UsedBytes, void *Handle)
151 tVirtIONet_Dev *NDev = VirtIO_GetDataPtr(Dev);
153 Semaphore_Signal(&NDev->RXPacketSem, 1);
154 // 1: Don't pop the qdesc
158 void VirtIONet_ReleaseRX(void *Arg, size_t HeadLen, size_t FootLen, const void *Data)
160 tVirtIO_Buf *buf = Arg;
161 tVirtIO_Dev *dev = VirtIO_GetBufferDev(buf);
163 VirtIO_ReleaseBuffer(buf);
164 void *bufptr = (void*)((tVAddr)Data & ~(PAGE_SIZE-1));
165 VirtIO_ReceiveBuffer(dev, 0, PAGE_SIZE, bufptr, NULL);
168 tIPStackBuffer *VirtIONet_WaitForPacket(void *Ptr)
170 tVirtIO_Dev *VIODev = Ptr;
171 tVirtIONet_Dev *NDev = VirtIO_GetDataPtr(VIODev);
173 if( Semaphore_Wait(&NDev->RXPacketSem, 1) != 1 ) {
179 tVirtIO_Buf *id = VirtIO_PopBuffer(VIODev, VIRTIONET_QUEUE_RX, &size, &buf);
182 // Pop failed, nothing there (possibly not error condition)
186 const tVirtIONet_PktHdr *hdr = buf;
187 int nbufs = (NDev->Features & VIRTIO_NET_F_MRG_RXBUF) ? hdr->NumBuffers : 1;
188 size_t dataofs = (NDev->Features & VIRTIO_NET_F_MRG_RXBUF) ? sizeof(*hdr) : sizeof(*hdr)-2;
190 ASSERTCR(nbufs, >=, 1, NULL);
191 ASSERTCR(size, >, dataofs, NULL);
193 tIPStackBuffer *ret = IPStack_Buffer_CreateBuffer(nbufs);
194 IPStack_Buffer_AppendSubBuffer(ret, 0, size-dataofs, (const char*)buf + dataofs, VirtIONet_ReleaseRX, id);
196 // TODO: This will break if descriptors end up being chained
198 for( int i = 1; i < nbufs; i ++ )
200 while( NULL == (id = VirtIO_PopBuffer(VIODev, VIRTIONET_QUEUE_RX, &size, &buf)) )
201 Semaphore_Wait(&NDev->RXPacketSem, 1);
202 IPStack_Buffer_AppendSubBuffer(ret, 0, size, buf, VirtIONet_ReleaseRX, id);
207 int VirtIONet_TXQueueCallback(tVirtIO_Dev *Dev, int ID, size_t UsedBytes, void *Handle)
210 LOG("Unlock TX'd buffer %p", Handle);
211 IPStack_Buffer_UnlockBuffer(Handle);
216 int VirtIONet_SendPacket(void *Ptr, tIPStackBuffer *Buffer)
218 tVirtIO_Dev *VIODev = Ptr;
219 tVirtIONet_Dev *NDev = VirtIO_GetDataPtr(VIODev);
222 for( int idx = -1; (idx = IPStack_Buffer_GetBuffer(Buffer, idx, NULL, NULL)) != -1; )
225 tVirtIONet_PktHdr hdr;
228 // GSO (TODO: Acess needs to support this)
232 // IP/TCP checksumming?
238 size_t buflens[1+nBufs];
239 const void *bufptrs[1+nBufs];
240 buflens[0] = sizeof(hdr) - ((NDev->Features & VIRTIO_NET_F_MRG_RXBUF) ? 0 : 2);
243 for( int idx = -1; (idx = IPStack_Buffer_GetBuffer(Buffer, idx, &buflens[i], &bufptrs[i])) != -1; ) {
244 //Debug_HexDump("VirtIO SendPacket", bufptrs[i], buflens[i]);
248 IPStack_Buffer_LockBuffer(Buffer);
249 VirtIO_SendBuffers(VIODev, VIRTIONET_QUEUE_TX, nBufs+1, buflens, bufptrs, Buffer);
251 // Wait until TX completes
252 IPStack_Buffer_LockBuffer(Buffer);
253 IPStack_Buffer_UnlockBuffer(Buffer);