| /**************************************************************************** |
| * fs/socket/socket.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <nuttx/kmalloc.h> |
| #include <nuttx/net/net.h> |
| #include <nuttx/fs/fs.h> |
| #include <nuttx/mm/mm.h> |
| |
| #include <sys/socket.h> |
| #include <assert.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include "inode/inode.h" |
| |
| /**************************************************************************** |
| * Private Functions Prototypes |
| ****************************************************************************/ |
| |
| static int sock_file_open(FAR struct file *filep); |
| static int sock_file_close(FAR struct file *filep); |
| static ssize_t sock_file_read(FAR struct file *filep, FAR char *buffer, |
| size_t buflen); |
| static ssize_t sock_file_write(FAR struct file *filep, |
| FAR const char *buffer, size_t buflen); |
| static int sock_file_ioctl(FAR struct file *filep, int cmd, |
| unsigned long arg); |
| static int sock_file_poll(FAR struct file *filep, struct pollfd *fds, |
| bool setup); |
| static int sock_file_truncate(FAR struct file *filep, off_t length); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static const struct file_operations g_sock_fileops = |
| { |
| sock_file_open, /* open */ |
| sock_file_close, /* close */ |
| sock_file_read, /* read */ |
| sock_file_write, /* write */ |
| NULL, /* seek */ |
| sock_file_ioctl, /* ioctl */ |
| NULL, /* mmap */ |
| sock_file_truncate, /* truncate */ |
| sock_file_poll /* poll */ |
| }; |
| |
| static struct inode g_sock_inode = |
| { |
| NULL, /* i_parent */ |
| NULL, /* i_peer */ |
| NULL, /* i_child */ |
| 1, /* i_crefs */ |
| FSNODEFLAG_TYPE_SOCKET, /* i_flags */ |
| { |
| &g_sock_fileops /* u */ |
| } |
| }; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| static int sock_file_open(FAR struct file *filep) |
| { |
| FAR struct socket *psock; |
| int ret; |
| |
| psock = kmm_zalloc(sizeof(*psock)); |
| if (psock == NULL) |
| { |
| return -ENOMEM; |
| } |
| |
| ret = psock_dup2(filep->f_priv, psock); |
| if (ret >= 0) |
| { |
| filep->f_priv = psock; |
| } |
| else |
| { |
| kmm_free(psock); |
| } |
| |
| return ret; |
| } |
| |
| static int sock_file_close(FAR struct file *filep) |
| { |
| psock_close(filep->f_priv); |
| kmm_free(filep->f_priv); |
| return 0; |
| } |
| |
| static ssize_t sock_file_read(FAR struct file *filep, FAR char *buffer, |
| size_t buflen) |
| { |
| return psock_recv(filep->f_priv, buffer, buflen, 0); |
| } |
| |
| static ssize_t sock_file_write(FAR struct file *filep, |
| FAR const char *buffer, size_t buflen) |
| { |
| return psock_send(filep->f_priv, buffer, buflen, 0); |
| } |
| |
| static int sock_file_ioctl(FAR struct file *filep, int cmd, |
| unsigned long arg) |
| { |
| return psock_ioctl(filep->f_priv, cmd, arg); |
| } |
| |
| static int sock_file_poll(FAR struct file *filep, FAR struct pollfd *fds, |
| bool setup) |
| { |
| return psock_poll(filep->f_priv, fds, setup); |
| } |
| |
| static int sock_file_truncate(FAR struct file *filep, off_t length) |
| { |
| return -EINVAL; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: sockfd_allocate |
| * |
| * Description: |
| * Allocate a socket descriptor |
| * |
| * Input Parameters: |
| * psock A pointer to socket structure. |
| * oflags Open mode flags. |
| * |
| * Returned Value: |
| * Allocate a struct files instance and associate it with an socket |
| * instance. Returns the file descriptor == index into the files array. |
| * |
| ****************************************************************************/ |
| |
| int sockfd_allocate(FAR struct socket *psock, int oflags) |
| { |
| return file_allocate(&g_sock_inode, oflags, 0, psock, 0, true); |
| } |
| |
| /**************************************************************************** |
| * Name: sockfd_socket |
| * |
| * Description: |
| * Given a socket descriptor, return the underlying socket structure. |
| * |
| * Input Parameters: |
| * sockfd - The socket descriptor index to use. |
| * |
| * Returns zero (OK) on success. On failure, it returns a negated errno |
| * value to indicate the nature of the error. |
| * |
| * EBADF |
| * The file descriptor is not a valid index in the descriptor table. |
| * ENOTSOCK |
| * psock is a descriptor for a file, not a socket. |
| * |
| ****************************************************************************/ |
| |
| FAR struct socket *file_socket(FAR struct file *filep) |
| { |
| if (filep != NULL && filep->f_inode != NULL && |
| INODE_IS_SOCKET(filep->f_inode)) |
| { |
| return filep->f_priv; |
| } |
| |
| return NULL; |
| } |
| |
| int sockfd_socket(int sockfd, FAR struct socket **socketp) |
| { |
| FAR struct file *filep; |
| |
| if (fs_getfilep(sockfd, &filep) < 0) |
| { |
| *socketp = NULL; |
| return -EBADF; |
| } |
| |
| *socketp = file_socket(filep); |
| |
| return *socketp != NULL ? OK : -ENOTSOCK; |
| } |
| |
| /**************************************************************************** |
| * Name: socket |
| * |
| * Description: |
| * socket() creates an endpoint for communication and returns a descriptor. |
| * |
| * Input Parameters: |
| * domain (see sys/socket.h) |
| * type (see sys/socket.h) |
| * protocol (see sys/socket.h) |
| * |
| * Returned Value: |
| * A non-negative socket descriptor on success; -1 on error with errno set |
| * appropriately. |
| * |
| * EACCES |
| * Permission to create a socket of the specified type and/or protocol |
| * is denied. |
| * EAFNOSUPPORT |
| * The implementation does not support the specified address family. |
| * EINVAL |
| * Unknown protocol, or protocol family not available. |
| * EMFILE |
| * Process file table overflow. |
| * ENFILE |
| * The system limit on the total number of open files has been reached. |
| * ENOBUFS or ENOMEM |
| * Insufficient memory is available. The socket cannot be created until |
| * sufficient resources are freed. |
| * EPROTONOSUPPORT |
| * The protocol type or the specified protocol is not supported within |
| * this domain. |
| * |
| * Assumptions: |
| * |
| ****************************************************************************/ |
| |
| int socket(int domain, int type, int protocol) |
| { |
| FAR struct socket *psock; |
| int oflags = O_RDWR; |
| int sockfd; |
| int ret; |
| |
| if (type & SOCK_CLOEXEC) |
| { |
| oflags |= O_CLOEXEC; |
| } |
| |
| if (type & SOCK_NONBLOCK) |
| { |
| oflags |= O_NONBLOCK; |
| } |
| |
| psock = kmm_zalloc(sizeof(*psock)); |
| if (psock == NULL) |
| { |
| ret = -ENOMEM; |
| goto errout; |
| } |
| |
| /* Initialize the socket structure */ |
| |
| ret = psock_socket(domain, type, protocol, psock); |
| if (ret < 0) |
| { |
| nerr("ERROR: psock_socket() failed: %d\n", ret); |
| goto errout_with_alloc; |
| } |
| |
| /* Allocate a socket descriptor */ |
| |
| sockfd = sockfd_allocate(psock, oflags); |
| if (sockfd < 0) |
| { |
| nerr("ERROR: Failed to allocate a socket descriptor\n"); |
| ret = sockfd; |
| goto errout_with_psock; |
| } |
| |
| return sockfd; |
| |
| errout_with_psock: |
| psock_close(psock); |
| |
| errout_with_alloc: |
| kmm_free(psock); |
| |
| errout: |
| set_errno(-ret); |
| return ERROR; |
| } |