Modules/VGA - Start on a generic VGA driver (VGA driver, and library for derivatives)
[tpg/acess2.git] / KernelLand / Modules / Network / VirtIONet / virtio-net.c
1 /*
2  * Acess2 VirtIO Network Driver
3  * - By John Hodge (thePowersGang)
4  *
5  * virtio-net.c
6  * - Driver Core
7  */
8 #define DEBUG   0
9 #define VERSION VER2(1,0)
10 #include <acess.h>
11 #include <modules.h>
12 #include <semaphore.h>
13 #include <drv_pci.h>
14 #include <IPStack/include/adapters_api.h>
15 #include "virtio-net.h"
16 #include <Libraries/VirtIO/include/virtio.h>
17
18 #define NRXBUFS 4
19
20 // === TYPEDEFS ===
21 typedef struct sVirtIONet_Dev   tVirtIONet_Dev;
22
23 // === STRUCTURES ===
24 struct sVirtIONet_Dev
25 {
26         Uint32  Features;
27         tSemaphore      RXPacketSem;
28         void    *RXBuffers[NRXBUFS];
29 };
30
31 // === PROTOTYPES ===
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);
39
40 // === GLOBALS ===
41 MODULE_DEFINE(0, VERSION, VirtIONet, VirtIONet_Install, VirtIONet_Cleanup, "IPStack", "VirtIOCommon", NULL);
42 tIPStack_AdapterType    gVirtIONet_AdapterType = {
43         .Name = "VirtIONet",
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
48 };
49
50 // === CODE ===
51 int VirtIONet_Install(char **Arguments)
52 {
53          int    pcidev = -1;
54         // Find network devices
55         while( (pcidev = PCI_GetDeviceByClass(0x020000, 0xFF0000, pcidev)) != -1 )
56         {
57                 Uint16  ven, dev;
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 )
62                         continue ;
63                 Uint16  subsys_id;
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);
67                         continue ;
68                 }
69
70                 Uint8   irq = PCI_GetIRQ(pcidev);
71                 // TODO: Detect bad IRQ
72                 
73                 Uint32  iobase = PCI_GetBAR(pcidev, 0);
74                 if( iobase == 0 ) {
75                         // oops
76                 }
77                 
78                 VirtIONet_AddCard(iobase & ~1, irq);
79         }
80         return 0;
81 }
82
83 int VirtIONet_Cleanup(void)
84 {
85         Log_Warning("VirtIONet", "TODO: Clean up before module unload");
86         return 0;
87 }
88
89 void VirtIONet_AddCard(Uint16 IOBase, Uint IRQ)
90 {
91         ENTER("xMMIOBase xIRQ", IOBase, IRQ);
92         // Should be a VirtIO Network device
93         tVirtIO_Dev *dev = VirtIO_InitDev(
94                 IOBase, IRQ,
95                 VIRTIO_NET_F_MAC|VIRTIO_NET_F_STATUS
96                         |VIRTIO_NET_F_CSUM
97                         |VIRTIO_NET_F_MRG_RXBUF
98                         |VIRTIO_F_NOTIFY_ON_EMPTY,
99                 3,
100                 sizeof(struct sVirtIONet_Dev)
101                 );
102         if( !dev ) {
103                 // Oops?
104         }
105         tVirtIONet_Dev  *ndev = VirtIO_GetDataPtr(dev);
106         Semaphore_Init(&ndev->RXPacketSem, 0, NRXBUFS, "VirtIONet", "RXSem");
107         ndev->Features = VirtIO_GetFeatures(dev);
108         
109         Uint8   mac[6];
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",
114                         IOBase, IRQ,
115                         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
116         }
117         else {
118                 // x[2,6,A,E] xx xx xx xx xx
119                 mac[0] = 0x0A;
120                 mac[1] = 0xCE;
121                 mac[2] = 0x55;
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",
126                         IOBase, IRQ,
127                         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
128         }
129
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);
133
134         // Set up RX buffers
135         for( int i = 0; i < NRXBUFS; i ++ )
136         {
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);
140         }
141
142         // Register with IPStack
143         LOG("Register");
144         // TODO: Save the returned pointer to do deregister later       
145         IPStack_Adapter_Add(&gVirtIONet_AdapterType, dev, mac);
146         LEAVE('-');
147 }
148
149 int VirtIONet_RXQueueCallback(tVirtIO_Dev *Dev, int ID, size_t UsedBytes, void *Handle)
150 {
151         tVirtIONet_Dev  *NDev = VirtIO_GetDataPtr(Dev);
152         LOG("Signalling");
153         Semaphore_Signal(&NDev->RXPacketSem, 1);
154         // 1: Don't pop the qdesc
155         return 1;
156 }
157
158 void VirtIONet_ReleaseRX(void *Arg, size_t HeadLen, size_t FootLen, const void *Data)
159 {
160         tVirtIO_Buf     *buf = Arg;
161         tVirtIO_Dev     *dev = VirtIO_GetBufferDev(buf);
162         // Re-add RX buffer
163         VirtIO_ReleaseBuffer(buf);
164         void    *bufptr = (void*)((tVAddr)Data & ~(PAGE_SIZE-1));
165         VirtIO_ReceiveBuffer(dev, 0, PAGE_SIZE, bufptr, NULL);
166 }
167
168 tIPStackBuffer *VirtIONet_WaitForPacket(void *Ptr)
169 {
170         tVirtIO_Dev     *VIODev = Ptr;
171         tVirtIONet_Dev  *NDev = VirtIO_GetDataPtr(VIODev);
172         
173         if( Semaphore_Wait(&NDev->RXPacketSem, 1) != 1 ) {
174                 return NULL;
175         }
176         
177         size_t  size;
178         const void      *buf;
179         tVirtIO_Buf     *id = VirtIO_PopBuffer(VIODev, VIRTIONET_QUEUE_RX, &size, &buf);
180         if( id == NULL )
181         {
182                 // Pop failed, nothing there (possibly not error condition)
183                 return NULL;
184         }
185         
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;
189         
190         ASSERTCR(nbufs, >=, 1, NULL);
191         ASSERTCR(size, >, dataofs, NULL);
192
193         tIPStackBuffer  *ret = IPStack_Buffer_CreateBuffer(nbufs);
194         IPStack_Buffer_AppendSubBuffer(ret, 0, size-dataofs, (const char*)buf + dataofs, VirtIONet_ReleaseRX, id);
195
196         // TODO: This will break if descriptors end up being chained
197
198         for( int i = 1; i < nbufs; i ++ )
199         {
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);
203         }
204         return ret;
205 }
206
207 int VirtIONet_TXQueueCallback(tVirtIO_Dev *Dev, int ID, size_t UsedBytes, void *Handle)
208 {
209         if( Handle ) {
210                 LOG("Unlock TX'd buffer %p", Handle);
211                 IPStack_Buffer_UnlockBuffer(Handle);
212         }
213         return 0;
214 }
215
216 int VirtIONet_SendPacket(void *Ptr, tIPStackBuffer *Buffer)
217 {
218         tVirtIO_Dev     *VIODev = Ptr;
219         tVirtIONet_Dev  *NDev = VirtIO_GetDataPtr(VIODev);
220         
221          int    nBufs = 0;
222         for( int idx = -1; (idx = IPStack_Buffer_GetBuffer(Buffer, idx, NULL, NULL)) != -1; )
223                 nBufs ++;
224         
225         tVirtIONet_PktHdr       hdr;
226
227         hdr.Flags = 0;
228         // GSO (TODO: Acess needs to support this)
229         hdr.GSOType = 0;
230         hdr.HeaderLen = 0;
231         hdr.GSOSize = 0;
232         // IP/TCP checksumming?
233         hdr.CSumStart = 0;
234         hdr.CSumOffset = 0;
235         
236         hdr.NumBuffers = 0;
237         
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);
241         bufptrs[0] = &hdr;
242          int    i = 1;
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]);
245                 i ++;
246         }
247         
248         IPStack_Buffer_LockBuffer(Buffer);
249         VirtIO_SendBuffers(VIODev, VIRTIONET_QUEUE_TX, nBufs+1, buflens, bufptrs, Buffer);
250         
251         // Wait until TX completes
252         IPStack_Buffer_LockBuffer(Buffer);
253         IPStack_Buffer_UnlockBuffer(Buffer);
254         
255         return 0;
256 }
257

UCC git Repository :: git.ucc.asn.au