GithubHelp home page GithubHelp logo

libcsp / libcsp Goto Github PK

View Code? Open in Web Editor NEW
470.0 37.0 250.0 7.42 MB

Cubesat Space Protocol - A small network-layer delivery protocol designed for Cubesats

Home Page: http://libcsp.github.io/libcsp/

License: MIT License

Python 3.26% C 92.67% CMake 2.00% Meson 1.98% Shell 0.09%
c satellite protocol cubesat

libcsp's Introduction

CI Status

The Cubesat Space Protocol

CSP

Cubesat Space Protocol (CSP) is a small protocol stack written in C. CSP is designed to ease communication between distributed embedded systems in smaller networks, such as Cubesats. The design follows the TCP/IP model and includes a transport protocol, a routing protocol and several MAC-layer interfaces. The core of libcsp includes a router, a connection oriented socket API and message/connection pools.

The protocol is based on an very lightweight header containing both transport and network-layer information. Its implementation is designed for, but not limited to, embedded systems with very limited CPU and memory resources. The implementation is written in GNU C and is currently ported to run on FreeRTOS, Zephyr and Linux (POSIX).

The idea is to give sub-system developers of cubesats the same features of a TCP/IP stack, but without adding the huge overhead of the IP header. The small footprint and simple implementation allows a small 8-bit system to be fully connected on the network. This allows all subsystems to provide their services on the same network level, without any master node required. Using a service oriented architecture has several advantages compared to the traditional mater/slave topology used on many cubesats.

  • Standardised network protocol: All subsystems can communicate with eachother (multi-master)
  • Service loose coupling: Services maintain a relationship that minimizes dependencies between subsystems
  • Service abstraction: Beyond descriptions in the service contract, services hide logic from the outside world
  • Service reusability: Logic is divided into services with the intention of promoting reuse.
  • Service autonomy: Services have control over the logic they encapsulate.
  • Service Redundancy: Easily add redundant services to the bus
  • Reduces single point of failure: The complexity is moved from a single master node to several well defined services on the network

The implementation of libcsp is written with simplicity in mind, but it's compile time configuration allows it to have some rather advanced features as well.

Features

  • Thread safe Socket API
  • Router task with Quality of Services
  • Connection-oriented operation (RFC 908 and 1151).
  • Connection-less operation (similar to UDP)
  • ICMP-like requests such as ping and buffer status.
  • Loopback interface
  • Very Small Footprint in regards to code and memory required
  • Zero-copy buffer and queue system
  • Modular network interface system
  • OS abstraction, currently ported to: FreeRTOS, Zephyr, Linux
  • Broadcast traffic
  • Promiscuous mode

Documentation

The latest version of the /doc folder is compiled to HTML and hosted on:

libcsp.github.io/libcsp/

Contributing

Thank you for considering contributing to libcsp! We welcome contributions from the community to help improve and grow the project. Please take a moment to review our guidelines before opening a Pull Request!

Software license

The source code is available under MIT license, see LICENSE for license text

libcsp's People

Contributors

abestanis avatar arrooney avatar avinashraja98 avatar bfol avatar bjarkegosvig avatar danieldavidson avatar dimitrovand avatar dolmio avatar edvardxyz avatar fjmolinas avatar ilieviliya92 avatar jledet avatar johandc avatar kantatamura avatar kivkiv12345 avatar kristianbay avatar lykkeberg avatar mandrek44 avatar moonlight83340 avatar nickmateus11 avatar pr0me avatar sasataku avatar sibovg avatar sigmundklaa avatar silvawp avatar sorennohr avatar taylorr82 avatar troelsjessen avatar wright avatar yashi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

libcsp's Issues

Settle on a coding style

The CSP core team finally reached consensus on the placement of pointer asterisks :)

char *correct = "This is correct";
char * bad = "This is not";

Fix it in the entire codebase...

csp/csp_autoconfig.h: No such file or directory

Hi, I am trying to compile source code of hmac from crypto. But I received this error and checked the CSP folder for csp_autoconfig.h. I did not found this header file. Can anyone help me regarding this issue.

Increase MTU on packets and add fragmentation on layer 3

Original idea: Implement full support for data buffering and fragmentation. (TCP-a-like)

Proposed change: Increase MTU and provide automatic fragmentation on layer 3. (unless the DF flag is set). This leads to two new fields in the routing table. These fields may be set statically in the table compile time, or discovered by a traceroute.

  1. Max MTU
  2. Estimated round trip time.

Wrong return value in CSP_IF_TRXUV

WAS: Possible race condition in buffer system
Note to johan: (explain better later :))

Triton-2 # ~~~~0x5011d7c0 : 00 00 00 00 00 00 50 01 01 00 00 70 A0 82 00 00 |......P....p....|
0x5011d7d0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0x5011ee40 : 00 00 00 00 00 00 51 00 01 00 00 6F A0 82 00 00 |......Q....o....|
0x5011ee50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0x5011f200 : 00 00 00 00 00 00 50 01 01 00 00 72 A0 82 00 00 |......P....r....|
0x5011f210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
[01] csp_if_trxuv.c:87 Another disaster
0x5011ee40 : 00 00 00 00 00 00 51 00 06 00 10 00 72 A0 82 00 |......Q.....r...|
0x5011ee50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0x5011d180 : 00 00 00 00 00 00 50 01 01 00 00 6C A0 82 00 00 |......P....l....|
0x5011d190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
[01] csp_if_trxuv.c:87 Another disaster
0x5011cf00 : 00 00 00 00 00 00 51 00 06 00 10 00 70 A0 82 00 |......Q.....p...|
0x5011cf10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0x5011d7c0 : 00 00 00 00 00 00 50 01 01 00 00 70 A0 82 00 00 |......P....p....|
0x5011d7d0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
[01] csp_if_trxuv.c:87 Another disaster
0x5011cf00 : 00 00 00 00 00 00 51 00 06 00 10 00 70 A0 82 00 |......Q.....p...|
0x5011cf10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0x5011f200 : 00 00 00 00 00 00 50 01 01 00 00 00 6C A0 82 00 |......P.....l...|
0x5011f210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
[01] csp_if_trxuv.c:87 Another disaster
0x5011d7c0 : 00 00 00 00 00 00 51 00 06 00 10 00 00 6C A0 82 |......Q......l..|
0x5011d7d0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|

Reconfigurable routing table

API for altering routing table via "CMP".
Also, when receiving a "source routed" packet, update the routing table to learn next hop for the reply. (Learning)

Support multiple instances of interfaces

We generally only support a single instance of each interface type, e.g. only one CAN interface because the csp_iface_t structs are statically defined. The KISS interface has some code to support multiple UARTs, but it would be nice to generalize this to cover all interface types. It should be possible to have both multiple instances of the same type (e.g. two SocketCAN interfaces) and of the same type but with different drivers (e.g. a platform I2C controller and an external controller).

How to know when RDP-ack has been received?

Is there way to know with rdp-connections and current api when the sender has succesfully received ack for the sent packet? It seems csp_send doesn't wait for the ack before returning.

Rewrite buffer system

The current buffer system is overly complicated, and has non-deterministic allocation times because the pool is searched using a for loop.

If the buffer elements were organized as a singly linked list, we could allocate by grabbing the first element in the list, and deallocate by inserting last in the list. O(1) instead of O(n).

The fix should be very easy. Just change the csp_buffer_state_t to contain a *next pointer and add a head and tail pointer for insertion/deletion.

Make csp_buffer size availabe to drivers

The drivers typically have an input overflow check defined (for example I2C_MTU). It would be better if the defined buffer size in CSP could be available to the driver, so it could check against the actual buffer size...

Increase the address space

We have started meeting the limitations to the CSP 1.0 header. I would like to start a discussion about where to go with version 2.0, and would like to start with some requirements:

  1. CSP 2.0 should be backwards compatible with CSP 1.0: This means that a CSP 1.0 message must be valid on any 2.0 node, and a 2.0 message should be valid on a 1.x node. I don't know if this can be fixed somehow using the existing code, but we should at least start now by adding a version field to the reserved bits and ensure that CSP from version 1.3 (when it's released) is capable of distinguishing between packet formats.
  2. CSP 2.0 should have a larger address space allowing for inter satellite networks, and maybe even a possible ground network extension (attaching a satellite ID or similar). Maybe we can take a look at the CCSDS recommendations for IPv4 in satellites, i don't know if there is some inspiration or not.

Proper packing

We're approaching CSP version 1.0, so we should start focusing on proper packing of the library. We need to:

  • Go through the documentation. Make sure everything is in doxygen format
  • Create README, INSTALL and LICENSE files
  • Make a good makefile for compiling the library without Eclipse. Could the GNU Autotools be used?
  • Create an examples dir with a running example

Implement conn table as open list and closed list

The current table lookup for connections, scans the entire connection list, even closes connections are checked before looping to the next element. In CSP the number of active open connections should be rather small, therefore it would make sense to make a linked list of open connections, rather than using the table itself. This will cost a bit more memory if pre-allocated connection objects are chosen, but it would be quicker. It also allows for a more relaxed use of malloc where a number of statically pre-allocated elements can be inserted into a connection-closed list during initialisation from which new connection objects can be found. When this list is empty, there could be a flag which indicates wether or not to allow extension of the list using malloc. A flag could be added to the connection object which tells the conn_close() handler wether or not the object is allowed to be free'd or if it should be put back on the connection-closed list.

Storage interface for settings

The routing table in CSP should be available as a struct array, that could be used by the user to easily store and restore the configuration to/from EEPROM or in flash. This is to allow generic CSP products to be shipped out, configured by the user and then stored in the flash configuraiton.

I suggest that CSP will support a callback function pattern for store and restore. CSP will take care of formatting and parsing the data, and the user provides a function that will store the char * data and int length, from CSP.

This may even be extended in the future such that node address, buffer sizes and other options could be stored along with the routing table, and make CSP even more flexible for the users.

The aim is to provide a generic CSP software for products like the EPS that we will "mass-produce" without re-compiling the software for each user-defined routing table.

Replay attack protection

The current HMAC implementation is vulnerable to replay attacks. A sequence number like in the IPsec AH should be added to protect against this.

Custom endpoints in ZMQ-Hub interface

Hi! First of all, I want to thank you for this wonderful project! It has really fitted my needs and I would be very happy if I can give you back some improvements.

I would like to introduce some changes in the ZMQ-Hub interface, to allow for more flexibility, so I'll need some guidance from you. Based on your feedback, I'll send you a PR with the changes.

For starters, I think it would be better to pass the two full endpoints to csp_zmqhub_init (both publisher and subscriber), instead of just the host. This would allow to use any zmq supported transport (eg. IPC), instead of just TCP, and remove hardcoded ports (6000, 7000).

By the way, is there any reason this interface outputs various errors using printf directly? Wouldn't it be better to use csp log functions instead? If not, would you consider at least sending output to stderr?

Thanks for your time!

Add i2c_frame_t to csp_if_i2c.h

This structure is currently only in i2c.h which is not included.

Also consider adding a header file for the I2C driver calls.

Move architecture specific code to src/arch

The src/arch dir contains architecture specific code for task create, semaphores and so on. However, some of our code still contains constructs like (csp_service_handler.c):

#if defined(CSP_FREERTOS)
        /* Try to malloc a lot */
        uint32_t size = 1000000, total = 0, max = UINT32_MAX;
        void * pmem;
        while (1) {
            pmem = csp_malloc(size + total);
            if (pmem == NULL) {
                max = size + total;
                size = size / 2;
            } else {
                total += size;
                if (total + size >= max)
                    size = size / 2;
                csp_free(pmem);
            }
            if (size < 32) break;
        }
#elif defined(CSP_POSIX)
        /* Read system statistics */
        uint32_t total = 0;
        struct sysinfo info;
        sysinfo(&info);
        total = info.freeram * info.mem_unit;
#elif defined(CSP_MACOSX)
        uint32_t total = 0; /* TODO: Fix memory free on OSX */
#elif defined(CSP_WINDOWS)
        MEMORYSTATUSEX statex;
        statex.dwLength = sizeof(statex);
        GlobalMemoryStatusEx(&statex);
        DWORDLONG freePhysicalMem = statex.ullAvailPhys;
        size_t total = (size_t) freePhysicalMem;
#endif

which looks awful...

This functionality should be moved to four functions in the src/arch dir.

Fine grained csp_debug output filtering

Current, all output from csp_debug can be disabled at compile time with the --disable-output configure switch. Some systems may wish to keep e.g. warning and error messages, but not use space for storing the strings for the remaining log levels.

I propose that we create csp_error, csp_warning, csp_info, etc. macros, and create a --with-loglevel configure switch, and let the preprocessor remove unwanted log levels.

BTW, the AVR debug output hack in csp.h also requires fixing. It should be able to use colored output and proper filtering.

CSP_MAX_DELAY should be CSP_INFINITY

Use the FreeRTOS function to never timeout if portMAX_DELAY (CSP_MAX_DELAY) is passed to a queue or semaphore.
This is fixed for FreeRTOS, but posix just uses a large number.
Posix should not use a time semaphore if MAX_DELAY is passed.

Finally, CSP_MAX_DELAY could be renamed to CSP_INFINITY in order to prevent confusion, but this is breaking backwards compatability...

Simplify CAN configuration

The fields in the can_at90can128_conf, can_socketcan_conf, can_at91sam7a1_conf and can_at91sam7a3_conf structs should be aggregated into a common can_config struct. This would avoid the ugly void pointer casts, sizeof(...), etc.

waf configure, program [...] is not executable

When trying to configure csp with avr32 toolchain and freertos, by following the procedure described in https://github.com/GomSpace/libcsp/blob/master/INSTALL.rst, the waf system produces the following error message: "Program ['avr32-gcc'] is not executable".

I have earlier successfully configured and build csp for avr32 using older version of waf (v.1.6.9) and wscript (1.0.1), but I'm not able to get the current versions to work.

What do I need to do to build csp with freertos for the avr32 toolchain with the current version of csp?
config_log.txt

csp_send return value

Unfortunately, the original version of CSP used 1 to indicate successful transmission in interface tx functions, and 0 to indicate failure. In the name of backward compatibility, this return value has been kept.

The remaining CSP calls return one of the defines from csp_error.h, which use 0 for success, and negative integer error codes. This gives an irregular API, and could lead to confusion since e.g. csp_send(...) == CSP_ERR_NONE is true when the transfer failed.

We can easily change the interface tx functions to return error codes. I suggest we also change the API for v1.1 so csp_send and interface tx functions return CSP_ERR_NONE on success like the rest of the API.

Add zmqhub service

The csp_if_zmqhub interface isn't of much use without the matching zmqhub service. Clean it up and push it to the public tree.

Add I2C and KISS interfaces

The interfaces for I2C and KISS are currently kept in the closed GomSpace repositories. We should clean them up and release them to public use for v1.1

1.0-RC1 Testing

Make a list of features and do systematic test of the release candidate.

  • Services:
    • CSP_PING
    • CSP_PS
    • CSP_MEMFREE
    • CSP_REBOOT
    • CSP_BUF_FREE
    • CSP_UPTIME
    • CSP_STATS
  • Features:
    • RDP
    • CRC32
    • HMAC
    • XTEA
  • Platforms:
    • FreeRTOS
    • Posix
  • Interfaces:
    • Loopback

Do not print log messages in ISR context

The functions ending in _isr are meant to run in interrupt context only. However, some of them contains log messages. Since interrupt stacks are often very limited, it is generally a no-go to do printf's in ISR context.

I just hit a couple of reboots on AVR due to csp_buffer_get_isr() doing a csp_log_error("Out of buffers\r\n") when it runs out of buffers.

I'm not sure what the correct solution to this is. Maybe we could come up with something like trace_printk?

CSP_MEMFREE gives wrong answer on freertos AVR due to truncation

csp_service_handler gives wrong answer on the CSP_MEMFREE port for freertos on AVR.

This is due to truncation of the argument given to malloc. Malloc takes type size_t, which is only two bytes on AVR. The value of 'size' is initialized to 1000000 and is thus truncated in the call to malloc.

Setting the initialized value of 'size' and 'max' to a value below UINT16_MAX results in the right answer.

Update the Wiki

The wiki page(s) should be updated. Furthermore, we could add pages with:

  • How to write a CSP interface
  • How to define protocols on top of CSP
  • Probably more...

I2C message receive

I am confused as to how packets should be received over I2C.

The i2c interface defines the following:
/**

  • When a frame is received, cast it to a csp_packet
  • and send it directly to the CSP new packet function.
  • Context: ISR only
  • @param frame
    */
    void csp_i2c_rx(i2c_frame_t * frame, void * pxTaskWoken)

Should this be called by the ISR after an entire packet has been received?
Does that mean that we have to manually reconstruct the frame (length, etc) before calling this function?
What is the parameter- pxtaskwoken?

Meanwhile: I remember having read somewhere that to make the I2C driver I only need to define the i2c_init/send/recv functions. What is the prototype for i2c_recv? Its not there in i2c.h.

CAN/ driver doesn't receive interface! multiple can interfaces on the same microcontroller not supported.

Hi!, I have implemented our drivers for your can interface, and it is working perfectly, but I have found an issue:
We have 2 can peripherals in our micro-controller, so we need to create 2 can interfaces, and route them independently to different destinations.
I think this is happening because all of your hardware nodes only have "one" interface of each type. And yo have never deal with this issue.
To fix this problem some modifications needs to be done:

  1. User needs to be able to create several instances of an interface. Right now interface is just a global variable declared in csp_if_can.c. Each interface would have at list a different name "can0", "can1".

  2. "can_send" function needs to receive as parameter a pointer to the interface. And also the tx_callback. Right now I see you are receiving the interface in the csp_can_tx function, but you don't use it for nothing!

  3. csp_can_init function, maybe should be split into 2 functions, one to be called only once for initializing queues and rxtask, and other to be called once for each interface....

If you are ok, I can try to do some refactor to implement this modifications, and that way I will be more sure about all modifications involved in fixing this issue. And the I show you the results. I won't be able to test all the drivers! I will only modify the signature of them.

I would like to know what do you think about this. Maybe you are already working on this...

Best Regards! Nicolas.

unknown error csp_init

I am trying implement csp for stm32f4+freertos. csp with newest freertos 8.1.2 not building, so I use well know for me 7.3.0 version. I got autoconf file. By using simple example project compiles up to csp_init where I got unknown error, Eclipse just shown Error1. if i comment lines with csp_conn_init() and csp_route_table_init(); it compiles but of course csp not working. so I come until csp_log_error("No more memory for sport semaphore\r\n"); and I don't know how to fix it. Please help me to solve it. Thank You.

set csp_prio field per packet, not per connection.

Today only one csp_prio may be selected for each connection using the csp_connect call. It should be possible to set different priorities on packets in the same connection. So the prio field should be added to csp_send, or we may extend the api with csp_send_prio(prio, conn, packet) which will then override the default priority set in the connection.

CSP debug should not enter do_csp_debug unless level is active

Entering do_csp_debug uses a va_list for arguments of arbitrary size. This is extremely ineffective especially on platforms such as AVR8. Therefore we should move the debug-level check outside of the do_csp_debug function, so we don't call it unnecessarily.

Generic Task Interface

Currently we have two different task interfaces as shown in the example below:

#ifndef CSP_WINDOWS
   int csp_thread_create(csp_thread_return_t (* routine)(void *), const signed char * const thread_name, unsigned short stack_depth, void * parameters, unsigned int priority, csp_thread_handle_t * handle);
#else
   int csp_thread_create(csp_thread_return_t (* routine)(void *)__attribute__((stdcall)), const signed char * const thread_name, unsigned short stack_depth, void * parameters, unsigned int priority, csp_thread_handle_t * handle);
#endif

We should define a CSP_TASK or CSP_THREAD macro instead that generates the right function prototype.

Source Routing

Before venturing into the great worlds of routing protocols, the simple tool of source routing should be added to CSP. This ensures that as long as nodes are informed of their neighboring nodes, a packet can always be successfully routed with manual source routing. This could be used as an emergency tool in order to modify routing tables that are invalid manually.

The implementation is quite simple, and not something that would interfere with normal CSP traffic. I suggest that we add a CSP_FSRCRTE flag to the header and add an optional routing table at the end of the data, just as we add other fields at the end.

When transmitting: The routing table could consist of a 8-bit length field and a variable length array of 8-bit fields which specify the next hop address. Each intermediate node will find its own address in the table, and send the message to the next hop node. When the message arrives at the CSP end-node, there are a few options

When receiving: If a message arrived to the node by source routing, we can assume that the default route on the receiving node is no longer valid, or there may be some other reason not to use the default route. Therefore the receiving node should either remember the source route on a connection basis, and/or update it's routing table to the correct default route. Maybe there should be a "learn" flag on the source routed message, that would make all CSP-nodes update their routing table. In this case source routing could be used as a tool to update the routing tables in the network by simply sending a ping message to a specific node, using source routing and the "learn" flag.

For the first implementation i think that the connection handler should remember the incoming source route, and then reverse it for replies on the connection. Only one problem. Since all connection data is static, how do we get memory allocation for an arbitrary length source route?

Add Windows and Mac OS X support

Add support for windows. Dan Erik Holmstrøm has added preliminary windows support here https://github.com/danerik/libcsp, but we still need e.g. a serial port driver.

While we're at it, we might as well try to add Mac OS X support. The current posix branch doesn't compile because OS X lacks the pthread semaphore/mutex realtime extensions. We can probably find a workaround.

CRC doesn't protect headers

Related to #45, CSP does not include the header when calculating the checksum. We cannot easily fix it, because CSP would then break compatability with itself.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.