Index: projects/clang390-import/contrib/gcclibs/libcpp/system.h =================================================================== --- projects/clang390-import/contrib/gcclibs/libcpp/system.h (revision 305430) +++ projects/clang390-import/contrib/gcclibs/libcpp/system.h (revision 305431) @@ -1,425 +1,428 @@ /* Get common system includes and various definitions and declarations based on autoconf macros. Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of libcpp (aka cpplib). GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LIBCPP_SYSTEM_H #define LIBCPP_SYSTEM_H /* We must include stdarg.h before stdio.h. */ #include #ifdef HAVE_STDDEF_H # include #endif #include /* Define a generic NULL if one hasn't already been defined. */ #ifndef NULL #define NULL 0 #endif /* Use the unlocked open routines from libiberty. */ #define fopen(PATH,MODE) fopen_unlocked(PATH,MODE) #define fdopen(FILDES,MODE) fdopen_unlocked(FILDES,MODE) #define freopen(PATH,MODE,STREAM) freopen_unlocked(PATH,MODE,STREAM) /* The compiler is not a multi-threaded application and therefore we do not have to use the locking functions. In fact, using the locking functions can cause the compiler to be significantly slower under I/O bound conditions (such as -g -O0 on very large source files). HAVE_DECL_PUTC_UNLOCKED actually indicates whether or not the stdio code is multi-thread safe by default. If it is set to 0, then do not worry about using the _unlocked functions. fputs_unlocked, fwrite_unlocked, and fprintf_unlocked are extensions and need to be prototyped by hand (since we do not define _GNU_SOURCE). */ #if defined HAVE_DECL_PUTC_UNLOCKED && HAVE_DECL_PUTC_UNLOCKED # ifdef HAVE_PUTC_UNLOCKED # undef putc # define putc(C, Stream) putc_unlocked (C, Stream) # endif # ifdef HAVE_PUTCHAR_UNLOCKED # undef putchar # define putchar(C) putchar_unlocked (C) # endif # ifdef HAVE_GETC_UNLOCKED # undef getc # define getc(Stream) getc_unlocked (Stream) # endif # ifdef HAVE_GETCHAR_UNLOCKED # undef getchar # define getchar() getchar_unlocked () # endif # ifdef HAVE_FPUTC_UNLOCKED # undef fputc # define fputc(C, Stream) fputc_unlocked (C, Stream) # endif # ifdef HAVE_CLEARERR_UNLOCKED # undef clearerr # define clearerr(Stream) clearerr_unlocked (Stream) # if defined (HAVE_DECL_CLEARERR_UNLOCKED) && !HAVE_DECL_CLEARERR_UNLOCKED extern void clearerr_unlocked (FILE *); # endif # endif # ifdef HAVE_FEOF_UNLOCKED # undef feof # define feof(Stream) feof_unlocked (Stream) # if defined (HAVE_DECL_FEOF_UNLOCKED) && !HAVE_DECL_FEOF_UNLOCKED extern int feof_unlocked (FILE *); # endif # endif # ifdef HAVE_FILENO_UNLOCKED # undef fileno # define fileno(Stream) fileno_unlocked (Stream) # if defined (HAVE_DECL_FILENO_UNLOCKED) && !HAVE_DECL_FILENO_UNLOCKED extern int fileno_unlocked (FILE *); # endif # endif # ifdef HAVE_FFLUSH_UNLOCKED # undef fflush # define fflush(Stream) fflush_unlocked (Stream) # if defined (HAVE_DECL_FFLUSH_UNLOCKED) && !HAVE_DECL_FFLUSH_UNLOCKED extern int fflush_unlocked (FILE *); # endif # endif # ifdef HAVE_FGETC_UNLOCKED # undef fgetc # define fgetc(Stream) fgetc_unlocked (Stream) # if defined (HAVE_DECL_FGETC_UNLOCKED) && !HAVE_DECL_FGETC_UNLOCKED extern int fgetc_unlocked (FILE *); # endif # endif # ifdef HAVE_FGETS_UNLOCKED # undef fgets # define fgets(S, n, Stream) fgets_unlocked (S, n, Stream) # if defined (HAVE_DECL_FGETS_UNLOCKED) && !HAVE_DECL_FGETS_UNLOCKED extern char *fgets_unlocked (char *, int, FILE *); # endif # endif # ifdef HAVE_FPUTS_UNLOCKED # undef fputs # define fputs(String, Stream) fputs_unlocked (String, Stream) # if defined (HAVE_DECL_FPUTS_UNLOCKED) && !HAVE_DECL_FPUTS_UNLOCKED extern int fputs_unlocked (const char *, FILE *); # endif # endif # ifdef HAVE_FERROR_UNLOCKED # undef ferror # define ferror(Stream) ferror_unlocked (Stream) # if defined (HAVE_DECL_FERROR_UNLOCKED) && !HAVE_DECL_FERROR_UNLOCKED extern int ferror_unlocked (FILE *); # endif # endif # ifdef HAVE_FREAD_UNLOCKED # undef fread # define fread(Ptr, Size, N, Stream) fread_unlocked (Ptr, Size, N, Stream) # if defined (HAVE_DECL_FREAD_UNLOCKED) && !HAVE_DECL_FREAD_UNLOCKED extern size_t fread_unlocked (void *, size_t, size_t, FILE *); # endif # endif # ifdef HAVE_FWRITE_UNLOCKED # undef fwrite # define fwrite(Ptr, Size, N, Stream) fwrite_unlocked (Ptr, Size, N, Stream) # if defined (HAVE_DECL_FWRITE_UNLOCKED) && !HAVE_DECL_FWRITE_UNLOCKED extern size_t fwrite_unlocked (const void *, size_t, size_t, FILE *); # endif # endif # ifdef HAVE_FPRINTF_UNLOCKED # undef fprintf /* We can't use a function-like macro here because we don't know if we have varargs macros. */ # define fprintf fprintf_unlocked # if defined (HAVE_DECL_FPRINTF_UNLOCKED) && !HAVE_DECL_FPRINTF_UNLOCKED extern int fprintf_unlocked (FILE *, const char *, ...); # endif # endif #endif /* ??? Glibc's fwrite/fread_unlocked macros cause "warning: signed and unsigned type in conditional expression". */ #undef fread_unlocked #undef fwrite_unlocked #include #include #if !defined (errno) && defined (HAVE_DECL_ERRNO) && !HAVE_DECL_ERRNO extern int errno; #endif /* Some of glibc's string inlines cause warnings. Plus we'd rather rely on (and therefore test) GCC's string builtins. */ #define __NO_STRING_INLINES #ifdef STRING_WITH_STRINGS # include # include #else # ifdef HAVE_STRING_H # include # else # ifdef HAVE_STRINGS_H # include # endif # endif #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #if HAVE_LIMITS_H # include #endif /* Infrastructure for defining missing _MAX and _MIN macros. Note that macros defined with these cannot be used in #if. */ /* The extra casts work around common compiler bugs. */ #define INTTYPE_SIGNED(t) (! ((t) 0 < (t) -1)) /* The outer cast is needed to work around a bug in Cray C 5.0.3.0. It is necessary at least when t == time_t. */ #define INTTYPE_MINIMUM(t) ((t) (INTTYPE_SIGNED (t) \ ? ~ (t) 0 << (sizeof(t) * CHAR_BIT - 1) : (t) 0)) #define INTTYPE_MAXIMUM(t) ((t) (~ (t) 0 - INTTYPE_MINIMUM (t))) /* Use that infrastructure to provide a few constants. */ #ifndef UCHAR_MAX # define UCHAR_MAX INTTYPE_MAXIMUM (unsigned char) #endif #ifdef TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # ifdef HAVE_TIME_H # include # endif # endif #endif #ifdef HAVE_FCNTL_H # include #else # ifdef HAVE_SYS_FILE_H # include # endif #endif #ifdef HAVE_LOCALE_H # include #endif #ifdef HAVE_LANGINFO_CODESET # include #endif #ifndef HAVE_SETLOCALE # define setlocale(category, locale) (locale) #endif #ifdef ENABLE_NLS #include #else /* Stubs. */ # undef dgettext # define dgettext(package, msgid) (msgid) #endif #ifndef _ # define _(msgid) dgettext (PACKAGE, msgid) #endif #ifndef N_ # define N_(msgid) msgid #endif /* Some systems define these in, e.g., param.h. We undefine these names here to avoid the warnings. We prefer to use our definitions since we know they are correct. */ #undef MIN #undef MAX #define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) #define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) /* The HAVE_DECL_* macros are three-state, undefined, 0 or 1. If they are defined to 0 then we must provide the relevant declaration here. These checks will be in the undefined state while configure is running so be careful to test "defined (HAVE_DECL_*)". */ #if defined (HAVE_DECL_ABORT) && !HAVE_DECL_ABORT extern void abort (void); #endif #if HAVE_SYS_STAT_H # include #endif /* Test if something is a normal file. */ #ifndef S_ISREG #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif /* Test if something is a directory. */ #ifndef S_ISDIR #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif /* Test if something is a character special file. */ #ifndef S_ISCHR #define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) #endif /* Test if something is a block special file. */ #ifndef S_ISBLK #define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) #endif /* Test if something is a socket. */ #ifndef S_ISSOCK # ifdef S_IFSOCK # define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) # else # define S_ISSOCK(m) 0 # endif #endif /* Test if something is a FIFO. */ #ifndef S_ISFIFO # ifdef S_IFIFO # define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) # else # define S_ISFIFO(m) 0 # endif #endif /* Approximate O_NOCTTY and O_BINARY. */ #ifndef O_NOCTTY #define O_NOCTTY 0 #endif #ifndef O_BINARY # define O_BINARY 0 #endif /* Filename handling macros. */ #include "filenames.h" /* Get libiberty declarations. */ #include "libiberty.h" #include "safe-ctype.h" /* 1 if we have C99 designated initializers. ??? C99 designated initializers are not supported by most C++ compilers, including G++. -- gdr, 2005-05-18 */ #if !defined(HAVE_DESIGNATED_INITIALIZERS) -#define HAVE_DESIGNATED_INITIALIZERS \ - ((!defined(__cplusplus) && (GCC_VERSION >= 2007)) \ - || (__STDC_VERSION__ >= 199901L)) +# if (!defined(__cplusplus) && (GCC_VERSION >= 2007)) \ + ||(__STDC_VERSION__ >= 199901L) +# define HAVE_DESIGNATED_INITIALIZERS 1 +# else +# define HAVE_DESIGNATED_INITIALIZERS 0 +# endif #endif /* Be conservative and only use enum bitfields with GCC. FIXME: provide a complete autoconf test for buggy enum bitfields. */ #if (GCC_VERSION > 2000) #define ENUM_BITFIELD(TYPE) __extension__ enum TYPE #else #define ENUM_BITFIELD(TYPE) unsigned int #endif #ifndef offsetof #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *) 0)->MEMBER) #endif /* __builtin_expect(A, B) evaluates to A, but notifies the compiler that the most likely value of A is B. This feature was added at some point between 2.95 and 3.0. Let's use 3.0 as the lower bound for now. */ #if (GCC_VERSION < 3000) #define __builtin_expect(a, b) (a) #endif /* Provide a fake boolean type. We make no attempt to use the C99 _Bool, as it may not be available in the bootstrap compiler, and even if it is, it is liable to be buggy. This must be after all inclusion of system headers, as some of them will mess us up. */ #undef bool #undef true #undef false #undef TRUE #undef FALSE #ifndef __cplusplus #define bool unsigned char #endif #define true 1 #define false 0 /* Some compilers do not allow the use of unsigned char in bitfields. */ #define BOOL_BITFIELD unsigned int /* Poison identifiers we do not want to use. */ #if (GCC_VERSION >= 3000) #undef calloc #undef strdup #undef malloc #undef realloc #pragma GCC poison calloc strdup #pragma GCC poison malloc realloc /* Libiberty macros that are no longer used in GCC. */ #undef ANSI_PROTOTYPES #undef PTR_CONST #undef LONG_DOUBLE #undef VPARAMS #undef VA_OPEN #undef VA_FIXEDARG #undef VA_CLOSE #undef VA_START #pragma GCC poison ANSI_PROTOTYPES PTR_CONST LONG_DOUBLE VPARAMS VA_OPEN \ VA_FIXEDARG VA_CLOSE VA_START /* Note: not all uses of the `index' token (e.g. variable names and structure members) have been eliminated. */ #undef bcopy #undef bzero #undef bcmp #undef rindex #pragma GCC poison bcopy bzero bcmp rindex #endif /* GCC >= 3.0 */ #endif /* ! LIBCPP_SYSTEM_H */ Index: projects/clang390-import/contrib/libarchive/README =================================================================== --- projects/clang390-import/contrib/libarchive/README (revision 305430) +++ projects/clang390-import/contrib/libarchive/README (nonexistent) @@ -1,163 +0,0 @@ -README for libarchive bundle. - -Questions? Issues? - * http://www.libarchive.org is the home for ongoing - libarchive development, including documentation, and - links to the libarchive mailing lists. - * To report an issue, use the issue tracker at - https://github.com/libarchive/libarchive/issues - * To submit an enhancement to libarchive, please submit - a pull request via GitHub. - https://github.com/libarchive/libarchive/pulls - -This distribution bundle includes the following components: - * libarchive: a library for reading and writing streaming archives - * tar: the 'bsdtar' program is a full-featured 'tar' - implementation built on libarchive - * cpio: the 'bsdcpio' program is a different interface to - essentially the same functionality - * cat: the 'bsdcat' program is a simple replacement tool for - zcat, bzcat, xzcat, and such - * examples: Some small example programs that you may find useful. - * examples/minitar: a compact sample demonstrating use of libarchive. - * contrib: Various items sent to me by third parties; - please contact the authors with any questions. - -The top-level directory contains the following information files: - * NEWS - highlights of recent changes - * COPYING - what you can do with this - * INSTALL - installation instructions - * README - this file - * configure - configuration script, see INSTALL for details. - * CMakeLists.txt - input for "cmake" build tool, see INSTALL - -The following files in the top-level directory are used by the -'configure' script: - * Makefile.am, aclocal.m4, configure.ac - - used to build this distribution, only needed by maintainers - * Makefile.in, config.h.in - - templates used by configure script - -Guide to Documentation installed by this system: - * bsdtar.1 explains the use of the bsdtar program - * bsdcpio.1 explains the use of the bsdcpio program - * bsdcat.1 explains the use of the bsdcat program - * libarchive.3 gives an overview of the library as a whole - * archive_read.3, archive_write.3, archive_write_disk.3, and - archive_read_disk.3 provide detailed calling sequences for the read - and write APIs - * archive_entry.3 details the "struct archive_entry" utility class - * archive_internals.3 provides some insight into libarchive's - internal structure and operation. - * libarchive-formats.5 documents the file formats supported by the library - * cpio.5, mtree.5, and tar.5 provide detailed information about these - popular archive formats, including hard-to-find details about - modern cpio and tar variants. -The manual pages above are provided in the 'doc' directory in -a number of different formats. - -You should also read the copious comments in "archive.h" and the -source code for the sample programs for more details. Please let us -know about any errors or omissions you find. - -Currently, the library automatically detects and reads the following fomats: - * GNU tar format (including GNU long filenames, long link names, and sparse files) - * Solaris 9 extended tar format (including ACLs) - * Old V7 tar archives - * POSIX ustar - * POSIX pax interchange format - * POSIX octet-oriented cpio - * SVR4 ASCII cpio - * POSIX octet-oriented cpio - * Binary cpio (big-endian or little-endian) - * ISO9660 CD-ROM images (with optional Rockridge or Joliet extensions) - * ZIP archives (with uncompressed or "deflate" compressed entries) - * GNU and BSD 'ar' archives - * 'mtree' format - * 7-Zip archives - * Microsoft CAB format - * LHA and LZH archives - * RAR archives - * XAR archives - -The library also detects and handles any of the following before evaluating the archive: - * uuencoded files - * files with RPM wrapper - * gzip compression - * bzip2 compression - * compress/LZW compression - * lzma, lzip, and xz compression - * lz4 compression - * lzop compression - -The library can create archives in any of the following formats: - * POSIX ustar - * POSIX pax interchange format - * "restricted" pax format, which will create ustar archives except for - entries that require pax extensions (for long filenames, ACLs, etc). - * Old GNU tar format - * Old V7 tar format - * POSIX octet-oriented cpio - * SVR4 "newc" cpio - * shar archives - * ZIP archives (with uncompressed or "deflate" compressed entries) - * GNU and BSD 'ar' archives - * 'mtree' format - * ISO9660 format - * 7-Zip archives - * XAR archives - -When creating archives, the result can be filtered with any of the following: - * uuencode - * gzip compression - * bzip2 compression - * compress/LZW compression - * lzma, lzip, and xz compression - * lz4 compression - * lzop compression - -Notes about the library architecture: - - * This is a heavily stream-oriented system. There is no direct - support for in-place modification or random access. - - * The library is designed to be extended with new compression and - archive formats. The only requirement is that the format be - readable or writable as a stream and that each archive entry be - independent. There are articles on the libarchive Wiki explaining - how to extend libarchive. - - * On read, compression and format are always detected automatically. - - * I've attempted to minimize static link pollution. If you don't - explicitly invoke a particular feature (such as support for a - particular compression or format), it won't get pulled in to - statically-linked programs. In particular, if you don't explicitly - enable a particular compression or decompression support, you won't - need to link against the corresponding compression or decompression - libraries. This also reduces the size of statically-linked - binaries in environments where that matters. - - * On read, the library accepts whatever blocks you hand it. - Your read callback is free to pass the library a byte at a time - or mmap the entire archive and give it to the library at once. - On write, the library always produces correctly-blocked output. - - * The object-style approach allows you to have multiple archive streams - open at once. bsdtar uses this in its "@archive" extension. - - * The archive itself is read/written using callback functions. - You can read an archive directly from an in-memory buffer or - write it to a socket, if you wish. There are some utility - functions to provide easy-to-use "open file," etc, capabilities. - - * The read/write APIs are designed to allow individual entries - to be read or written to any data source: You can create - a block of data in memory and add it to a tar archive without - first writing a temporary file. You can also read an entry from - an archive and write the data directly to a socket. If you want - to read/write entries to disk, there are convenience functions to - make this especially easy. - - * Note: "pax interchange format" is really an extended tar format, - despite what the name says. Property changes on: projects/clang390-import/contrib/libarchive/README ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: projects/clang390-import/contrib/libarchive/README.md =================================================================== --- projects/clang390-import/contrib/libarchive/README.md (nonexistent) +++ projects/clang390-import/contrib/libarchive/README.md (revision 305431) @@ -0,0 +1,222 @@ +# Welcome to libarchive! + +The libarchive project develops a portable, efficient C library that +can read and write streaming archives in a variety of formats. It +also includes implementations of the common `tar`, `cpio`, and `zcat` +command-line tools that use the libarchive library. + +## Questions? Issues? + +* http://www.libarchive.org is the home for ongoing + libarchive development, including documentation, + and links to the libarchive mailing lists. +* To report an issue, use the issue tracker at + https://github.com/libarchive/libarchive/issues +* To submit an enhancement to libarchive, please + submit a pull request via GitHub: https://github.com/libarchive/libarchive/pulls + +## Contents of the Distribution + +This distribution bundle includes the following major components: + +* **libarchive**: a library for reading and writing streaming archives +* **tar**: the 'bsdtar' program is a full-featured 'tar' implementation built on libarchive +* **cpio**: the 'bsdcpio' program is a different interface to essentially the same functionality +* **cat**: the 'bsdcat' program is a simple replacement tool for zcat, bzcat, xzcat, and such +* **examples**: Some small example programs that you may find useful. +* **examples/minitar**: a compact sample demonstrating use of libarchive. +* **contrib**: Various items sent to me by third parties; please contact the authors with any questions. + +The top-level directory contains the following information files: + +* **NEWS** - highlights of recent changes +* **COPYING** - what you can do with this +* **INSTALL** - installation instructions +* **README** - this file +* **CMakeLists.txt** - input for "cmake" build tool, see INSTALL +* **configure** - configuration script, see INSTALL for details. If your copy of the source lacks a `configure` script, you can try to construct it by running the script in `build/autogen.sh` (or use `cmake`). + +The following files in the top-level directory are used by the 'configure' script: +* `Makefile.am`, `aclocal.m4`, `configure.ac` - used to build this distribution, only needed by maintainers +* `Makefile.in`, `config.h.in` - templates used by configure script + +## Documentation + +In addition to the informational articles and documentation +in the online [libarchive Wiki](https://github.com/libarchive/libarchive/wiki), +the distribution also includes a number of manual pages: + + * bsdtar.1 explains the use of the bsdtar program + * bsdcpio.1 explains the use of the bsdcpio program + * bsdcat.1 explains the use of the bsdcat program + * libarchive.3 gives an overview of the library as a whole + * archive_read.3, archive_write.3, archive_write_disk.3, and + archive_read_disk.3 provide detailed calling sequences for the read + and write APIs + * archive_entry.3 details the "struct archive_entry" utility class + * archive_internals.3 provides some insight into libarchive's + internal structure and operation. + * libarchive-formats.5 documents the file formats supported by the library + * cpio.5, mtree.5, and tar.5 provide detailed information about these + popular archive formats, including hard-to-find details about + modern cpio and tar variants. + +The manual pages above are provided in the 'doc' directory in +a number of different formats. + +You should also read the copious comments in `archive.h` and the +source code for the sample programs for more details. Please let us +know about any errors or omissions you find. + +## Supported Formats + +Currently, the library automatically detects and reads the following fomats: + * Old V7 tar archives + * POSIX ustar + * GNU tar format (including GNU long filenames, long link names, and sparse files) + * Solaris 9 extended tar format (including ACLs) + * POSIX pax interchange format + * POSIX octet-oriented cpio + * SVR4 ASCII cpio + * POSIX octet-oriented cpio + * Binary cpio (big-endian or little-endian) + * ISO9660 CD-ROM images (with optional Rockridge or Joliet extensions) + * ZIP archives (with uncompressed or "deflate" compressed entries, including support for encrypted Zip archives) + * GNU and BSD 'ar' archives + * 'mtree' format + * 7-Zip archives + * Microsoft CAB format + * LHA and LZH archives + * RAR archives (with some limitations due to RAR's proprietary status) + * XAR archives + +The library also detects and handles any of the following before evaluating the archive: + * uuencoded files + * files with RPM wrapper + * gzip compression + * bzip2 compression + * compress/LZW compression + * lzma, lzip, and xz compression + * lz4 compression + * lzop compression + +The library can create archives in any of the following formats: + * POSIX ustar + * POSIX pax interchange format + * "restricted" pax format, which will create ustar archives except for + entries that require pax extensions (for long filenames, ACLs, etc). + * Old GNU tar format + * Old V7 tar format + * POSIX octet-oriented cpio + * SVR4 "newc" cpio + * shar archives + * ZIP archives (with uncompressed or "deflate" compressed entries) + * GNU and BSD 'ar' archives + * 'mtree' format + * ISO9660 format + * 7-Zip archives + * XAR archives + +When creating archives, the result can be filtered with any of the following: + * uuencode + * gzip compression + * bzip2 compression + * compress/LZW compression + * lzma, lzip, and xz compression + * lz4 compression + * lzop compression + +## Notes about the Library Design + +The following notes address many of the most common +questions we are asked about libarchive: + +* This is a heavily stream-oriented system. That means that + it is optimized to read or write the archive in a single + pass from beginning to end. For example, this allows + libarchive to process archives too large to store on disk + by processing them on-the-fly as they are read from or + written to a network or tape drive. This also makes + libarchive useful for tools that need to produce + archives on-the-fly (such as webservers that provide + archived contents of a users account). + +* In-place modification and random access to the contents + of an archive are not directly supported. For some formats, + this is not an issue: For example, tar.gz archives are not + designed for random access. In some other cases, libarchive + can re-open an archive and scan it from the beginning quickly + enough to provide the needed abilities even without true + random access. Of course, some applications do require true + random access; those applications should consider alternatives + to libarchive. + +* The library is designed to be extended with new compression and + archive formats. The only requirement is that the format be + readable or writable as a stream and that each archive entry be + independent. There are articles on the libarchive Wiki explaining + how to extend libarchive. + +* On read, compression and format are always detected automatically. + +* The same API is used for all formats; in particular, it's very + easy for software using libarchive to transparently handle + any of libarchive's archiving formats. + +* Libarchive's automatic support for decompression can be used + without archiving by explicitly selecting the "raw" and "empty" + formats. + +* I've attempted to minimize static link pollution. If you don't + explicitly invoke a particular feature (such as support for a + particular compression or format), it won't get pulled in to + statically-linked programs. In particular, if you don't explicitly + enable a particular compression or decompression support, you won't + need to link against the corresponding compression or decompression + libraries. This also reduces the size of statically-linked + binaries in environments where that matters. + +* The library is generally _thread safe_ depending on the platform: + it does not define any global variables of its own. However, some + platforms do not provide fully thread-safe versions of key C library + functions. On those platforms, libarchive will use the non-thread-safe + functions. Patches to improve this are of great interest to us. + +* In particular, libarchive's modules to read or write a directory + tree do use `chdir()` to optimize the directory traversals. This + can cause problems for programs that expect to do disk access from + multiple threads. Of course, those modules are completely + optional and you can use the rest of libarchive without them. + +* The library is _not_ thread aware, however. It does no locking + or thread management of any kind. If you create a libarchive + object and need to access it from multiple threads, you will + need to provide your own locking. + +* On read, the library accepts whatever blocks you hand it. + Your read callback is free to pass the library a byte at a time + or mmap the entire archive and give it to the library at once. + On write, the library always produces correctly-blocked output. + +* The object-style approach allows you to have multiple archive streams + open at once. bsdtar uses this in its "@archive" extension. + +* The archive itself is read/written using callback functions. + You can read an archive directly from an in-memory buffer or + write it to a socket, if you wish. There are some utility + functions to provide easy-to-use "open file," etc, capabilities. + +* The read/write APIs are designed to allow individual entries + to be read or written to any data source: You can create + a block of data in memory and add it to a tar archive without + first writing a temporary file. You can also read an entry from + an archive and write the data directly to a socket. If you want + to read/write entries to disk, there are convenience functions to + make this especially easy. + +* Note: The "pax interchange format" is a POSIX standard extended tar + format that should be used when the older _ustar_ format is not + appropriate. It has many advantages over other tar formats + (including the legacy GNU tar format) and is widely supported by + current tar implementations. + Index: projects/clang390-import/contrib/libarchive/libarchive/archive_acl.c =================================================================== --- projects/clang390-import/contrib/libarchive/libarchive/archive_acl.c (revision 305430) +++ projects/clang390-import/contrib/libarchive/libarchive/archive_acl.c (revision 305431) @@ -1,1278 +1,1279 @@ /*- * Copyright (c) 2003-2010 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_WCHAR_H #include #endif #include "archive_acl_private.h" #include "archive_entry.h" #include "archive_private.h" #undef max #define max(a, b) ((a)>(b)?(a):(b)) #ifndef HAVE_WMEMCMP /* Good enough for simple equality testing, but not for sorting. */ #define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t)) #endif static int acl_special(struct archive_acl *acl, int type, int permset, int tag); static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl, int type, int permset, int tag, int id); static int archive_acl_add_entry_len_l(struct archive_acl *acl, int type, int permset, int tag, int id, const char *name, size_t len, struct archive_string_conv *sc); static int isint_w(const wchar_t *start, const wchar_t *end, int *result); static int ismode_w(const wchar_t *start, const wchar_t *end, int *result); static void next_field_w(const wchar_t **wp, const wchar_t **start, const wchar_t **end, wchar_t *sep); static int prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test); static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, const wchar_t *wname, int perm, int id); static void append_id_w(wchar_t **wp, int id); static int isint(const char *start, const char *end, int *result); static int ismode(const char *start, const char *end, int *result); static void next_field(const char **p, const char **start, const char **end, char *sep); static int prefix_c(const char *start, const char *end, const char *test); static void append_entry(char **p, const char *prefix, int tag, const char *name, int perm, int id); static void append_id(char **p, int id); void archive_acl_clear(struct archive_acl *acl) { struct archive_acl_entry *ap; while (acl->acl_head != NULL) { ap = acl->acl_head->next; archive_mstring_clean(&acl->acl_head->name); free(acl->acl_head); acl->acl_head = ap; } if (acl->acl_text_w != NULL) { free(acl->acl_text_w); acl->acl_text_w = NULL; } if (acl->acl_text != NULL) { free(acl->acl_text); acl->acl_text = NULL; } acl->acl_p = NULL; acl->acl_state = 0; /* Not counting. */ } void archive_acl_copy(struct archive_acl *dest, struct archive_acl *src) { struct archive_acl_entry *ap, *ap2; archive_acl_clear(dest); dest->mode = src->mode; ap = src->acl_head; while (ap != NULL) { ap2 = acl_new_entry(dest, ap->type, ap->permset, ap->tag, ap->id); if (ap2 != NULL) archive_mstring_copy(&ap2->name, &ap->name); ap = ap->next; } } int archive_acl_add_entry(struct archive_acl *acl, int type, int permset, int tag, int id, const char *name) { struct archive_acl_entry *ap; if (acl_special(acl, type, permset, tag) == 0) return ARCHIVE_OK; ap = acl_new_entry(acl, type, permset, tag, id); if (ap == NULL) { /* XXX Error XXX */ return ARCHIVE_FAILED; } if (name != NULL && *name != '\0') archive_mstring_copy_mbs(&ap->name, name); else archive_mstring_clean(&ap->name); return ARCHIVE_OK; } int archive_acl_add_entry_w_len(struct archive_acl *acl, int type, int permset, int tag, int id, const wchar_t *name, size_t len) { struct archive_acl_entry *ap; if (acl_special(acl, type, permset, tag) == 0) return ARCHIVE_OK; ap = acl_new_entry(acl, type, permset, tag, id); if (ap == NULL) { /* XXX Error XXX */ return ARCHIVE_FAILED; } if (name != NULL && *name != L'\0' && len > 0) archive_mstring_copy_wcs_len(&ap->name, name, len); else archive_mstring_clean(&ap->name); return ARCHIVE_OK; } static int archive_acl_add_entry_len_l(struct archive_acl *acl, int type, int permset, int tag, int id, const char *name, size_t len, struct archive_string_conv *sc) { struct archive_acl_entry *ap; int r; if (acl_special(acl, type, permset, tag) == 0) return ARCHIVE_OK; ap = acl_new_entry(acl, type, permset, tag, id); if (ap == NULL) { /* XXX Error XXX */ return ARCHIVE_FAILED; } if (name != NULL && *name != '\0' && len > 0) { r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc); } else { r = 0; archive_mstring_clean(&ap->name); } if (r == 0) return (ARCHIVE_OK); else if (errno == ENOMEM) return (ARCHIVE_FATAL); else return (ARCHIVE_WARN); } /* * If this ACL entry is part of the standard POSIX permissions set, * store the permissions in the stat structure and return zero. */ static int acl_special(struct archive_acl *acl, int type, int permset, int tag) { if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS && ((permset & ~007) == 0)) { switch (tag) { case ARCHIVE_ENTRY_ACL_USER_OBJ: acl->mode &= ~0700; acl->mode |= (permset & 7) << 6; return (0); case ARCHIVE_ENTRY_ACL_GROUP_OBJ: acl->mode &= ~0070; acl->mode |= (permset & 7) << 3; return (0); case ARCHIVE_ENTRY_ACL_OTHER: acl->mode &= ~0007; acl->mode |= permset & 7; return (0); } } return (1); } /* * Allocate and populate a new ACL entry with everything but the * name. */ static struct archive_acl_entry * acl_new_entry(struct archive_acl *acl, int type, int permset, int tag, int id) { struct archive_acl_entry *ap, *aq; /* Type argument must be a valid NFS4 or POSIX.1e type. * The type must agree with anything already set and * the permset must be compatible. */ if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { return (NULL); } if (permset & ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4 | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) { return (NULL); } } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { return (NULL); } if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) { return (NULL); } } else { return (NULL); } /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */ switch (tag) { case ARCHIVE_ENTRY_ACL_USER: case ARCHIVE_ENTRY_ACL_USER_OBJ: case ARCHIVE_ENTRY_ACL_GROUP: case ARCHIVE_ENTRY_ACL_GROUP_OBJ: /* Tags valid in both NFS4 and POSIX.1e */ break; case ARCHIVE_ENTRY_ACL_MASK: case ARCHIVE_ENTRY_ACL_OTHER: /* Tags valid only in POSIX.1e. */ if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { return (NULL); } break; case ARCHIVE_ENTRY_ACL_EVERYONE: /* Tags valid only in NFS4. */ if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { return (NULL); } break; default: /* No other values are valid. */ return (NULL); } if (acl->acl_text_w != NULL) { free(acl->acl_text_w); acl->acl_text_w = NULL; } if (acl->acl_text != NULL) { free(acl->acl_text); acl->acl_text = NULL; } /* If there's a matching entry already in the list, overwrite it. */ ap = acl->acl_head; aq = NULL; while (ap != NULL) { if (ap->type == type && ap->tag == tag && ap->id == id) { ap->permset = permset; return (ap); } aq = ap; ap = ap->next; } /* Add a new entry to the end of the list. */ ap = (struct archive_acl_entry *)malloc(sizeof(*ap)); if (ap == NULL) return (NULL); memset(ap, 0, sizeof(*ap)); if (aq == NULL) acl->acl_head = ap; else aq->next = ap; ap->type = type; ap->tag = tag; ap->id = id; ap->permset = permset; acl->acl_types |= type; return (ap); } /* * Return a count of entries matching "want_type". */ int archive_acl_count(struct archive_acl *acl, int want_type) { int count; struct archive_acl_entry *ap; count = 0; ap = acl->acl_head; while (ap != NULL) { if ((ap->type & want_type) != 0) count++; ap = ap->next; } if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) count += 3; return (count); } /* * Prepare for reading entries from the ACL data. Returns a count * of entries matching "want_type", or zero if there are no * non-extended ACL entries of that type. */ int archive_acl_reset(struct archive_acl *acl, int want_type) { int count, cutoff; count = archive_acl_count(acl, want_type); /* * If the only entries are the three standard ones, * then don't return any ACL data. (In this case, * client can just use chmod(2) to set permissions.) */ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) cutoff = 3; else cutoff = 0; if (count > cutoff) acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ; else acl->acl_state = 0; acl->acl_p = acl->acl_head; return (count); } /* * Return the next ACL entry in the list. Fake entries for the * standard permissions and include them in the returned list. */ int archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type, int *permset, int *tag, int *id, const char **name) { *name = NULL; *id = -1; /* * The acl_state is either zero (no entries available), -1 * (reading from list), or an entry type (retrieve that type * from ae_stat.aest_mode). */ if (acl->acl_state == 0) return (ARCHIVE_WARN); /* The first three access entries are special. */ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { switch (acl->acl_state) { case ARCHIVE_ENTRY_ACL_USER_OBJ: *permset = (acl->mode >> 6) & 7; *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; *tag = ARCHIVE_ENTRY_ACL_USER_OBJ; acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ; return (ARCHIVE_OK); case ARCHIVE_ENTRY_ACL_GROUP_OBJ: *permset = (acl->mode >> 3) & 7; *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER; return (ARCHIVE_OK); case ARCHIVE_ENTRY_ACL_OTHER: *permset = acl->mode & 7; *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; *tag = ARCHIVE_ENTRY_ACL_OTHER; acl->acl_state = -1; acl->acl_p = acl->acl_head; return (ARCHIVE_OK); default: break; } } while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0) acl->acl_p = acl->acl_p->next; if (acl->acl_p == NULL) { acl->acl_state = 0; *type = 0; *permset = 0; *tag = 0; *id = -1; *name = NULL; return (ARCHIVE_EOF); /* End of ACL entries. */ } *type = acl->acl_p->type; *permset = acl->acl_p->permset; *tag = acl->acl_p->tag; *id = acl->acl_p->id; if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) { if (errno == ENOMEM) return (ARCHIVE_FATAL); *name = NULL; } acl->acl_p = acl->acl_p->next; return (ARCHIVE_OK); } /* * Generate a text version of the ACL. The flags parameter controls * the style of the generated ACL. */ const wchar_t * archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags) { int count; size_t length; const wchar_t *wname; const wchar_t *prefix; wchar_t separator; struct archive_acl_entry *ap; int id, r; wchar_t *wp; if (acl->acl_text_w != NULL) { free (acl->acl_text_w); acl->acl_text_w = NULL; } separator = L','; count = 0; length = 0; ap = acl->acl_head; while (ap != NULL) { if ((ap->type & flags) != 0) { count++; if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) length += 8; /* "default:" */ length += 5; /* tag name */ length += 1; /* colon */ r = archive_mstring_get_wcs(a, &ap->name, &wname); if (r == 0 && wname != NULL) length += wcslen(wname); else if (r < 0 && errno == ENOMEM) return (NULL); else length += sizeof(uid_t) * 3 + 1; length ++; /* colon */ length += 3; /* rwx */ length += 1; /* colon */ length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; length ++; /* newline */ } ap = ap->next; } if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { length += 10; /* "user::rwx\n" */ length += 11; /* "group::rwx\n" */ length += 11; /* "other::rwx\n" */ } if (count == 0) return (NULL); /* Now, allocate the string and actually populate it. */ wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t)); if (wp == NULL) return (NULL); count = 0; if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, acl->mode & 0700, -1); *wp++ = ','; append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, acl->mode & 0070, -1); *wp++ = ','; append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, acl->mode & 0007, -1); count += 3; ap = acl->acl_head; while (ap != NULL) { if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { r = archive_mstring_get_wcs(a, &ap->name, &wname); if (r == 0) { *wp++ = separator; if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) id = ap->id; else id = -1; append_entry_w(&wp, NULL, ap->tag, wname, ap->permset, id); count++; } else if (r < 0 && errno == ENOMEM) return (NULL); } ap = ap->next; } } if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) prefix = L"default:"; else prefix = NULL; ap = acl->acl_head; count = 0; while (ap != NULL) { if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { r = archive_mstring_get_wcs(a, &ap->name, &wname); if (r == 0) { if (count > 0) *wp++ = separator; if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) id = ap->id; else id = -1; append_entry_w(&wp, prefix, ap->tag, wname, ap->permset, id); count ++; } else if (r < 0 && errno == ENOMEM) return (NULL); } ap = ap->next; } } return (acl->acl_text_w); } static void append_id_w(wchar_t **wp, int id) { if (id < 0) id = 0; if (id > 9) append_id_w(wp, id / 10); *(*wp)++ = L"0123456789"[id % 10]; } static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, const wchar_t *wname, int perm, int id) { if (prefix != NULL) { wcscpy(*wp, prefix); *wp += wcslen(*wp); } switch (tag) { case ARCHIVE_ENTRY_ACL_USER_OBJ: wname = NULL; id = -1; /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: wcscpy(*wp, L"user"); break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: wname = NULL; id = -1; /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_GROUP: wcscpy(*wp, L"group"); break; case ARCHIVE_ENTRY_ACL_MASK: wcscpy(*wp, L"mask"); wname = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_OTHER: wcscpy(*wp, L"other"); wname = NULL; id = -1; break; } *wp += wcslen(*wp); *(*wp)++ = L':'; if (wname != NULL) { wcscpy(*wp, wname); *wp += wcslen(*wp); } else if (tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { append_id_w(wp, id); id = -1; } *(*wp)++ = L':'; *(*wp)++ = (perm & 0444) ? L'r' : L'-'; *(*wp)++ = (perm & 0222) ? L'w' : L'-'; *(*wp)++ = (perm & 0111) ? L'x' : L'-'; if (id != -1) { *(*wp)++ = L':'; append_id_w(wp, id); } **wp = L'\0'; } int archive_acl_text_l(struct archive_acl *acl, int flags, const char **acl_text, size_t *acl_text_len, struct archive_string_conv *sc) { int count; size_t length; const char *name; const char *prefix; char separator; struct archive_acl_entry *ap; size_t len; int id, r; char *p; if (acl->acl_text != NULL) { free (acl->acl_text); acl->acl_text = NULL; } *acl_text = NULL; if (acl_text_len != NULL) *acl_text_len = 0; separator = ','; count = 0; length = 0; ap = acl->acl_head; while (ap != NULL) { if ((ap->type & flags) != 0) { count++; if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) length += 8; /* "default:" */ length += 5; /* tag name */ length += 1; /* colon */ r = archive_mstring_get_mbs_l( &ap->name, &name, &len, sc); if (r != 0) return (-1); if (len > 0 && name != NULL) length += len; else length += sizeof(uid_t) * 3 + 1; length ++; /* colon */ length += 3; /* rwx */ length += 1; /* colon */ length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; length ++; /* newline */ } ap = ap->next; } if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { length += 10; /* "user::rwx\n" */ length += 11; /* "group::rwx\n" */ length += 11; /* "other::rwx\n" */ } if (count == 0) return (0); /* Now, allocate the string and actually populate it. */ p = acl->acl_text = (char *)malloc(length); if (p == NULL) return (-1); count = 0; if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, acl->mode & 0700, -1); *p++ = ','; append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, acl->mode & 0070, -1); *p++ = ','; append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, acl->mode & 0007, -1); count += 3; for (ap = acl->acl_head; ap != NULL; ap = ap->next) { if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0) continue; r = archive_mstring_get_mbs_l( &ap->name, &name, &len, sc); if (r != 0) return (-1); *p++ = separator; - if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) + if (name == NULL || (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) { id = ap->id; - else + } else { id = -1; + } append_entry(&p, NULL, ap->tag, name, ap->permset, id); count++; } } if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) prefix = "default:"; else prefix = NULL; count = 0; for (ap = acl->acl_head; ap != NULL; ap = ap->next) { if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0) continue; r = archive_mstring_get_mbs_l( &ap->name, &name, &len, sc); if (r != 0) return (-1); if (count > 0) *p++ = separator; if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) id = ap->id; else id = -1; append_entry(&p, prefix, ap->tag, name, ap->permset, id); count ++; } } *acl_text = acl->acl_text; if (acl_text_len != NULL) *acl_text_len = strlen(acl->acl_text); return (0); } static void append_id(char **p, int id) { if (id < 0) id = 0; if (id > 9) append_id(p, id / 10); *(*p)++ = "0123456789"[id % 10]; } static void append_entry(char **p, const char *prefix, int tag, const char *name, int perm, int id) { if (prefix != NULL) { strcpy(*p, prefix); *p += strlen(*p); } switch (tag) { case ARCHIVE_ENTRY_ACL_USER_OBJ: name = NULL; id = -1; /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: strcpy(*p, "user"); break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: name = NULL; id = -1; /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_GROUP: strcpy(*p, "group"); break; case ARCHIVE_ENTRY_ACL_MASK: strcpy(*p, "mask"); name = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_OTHER: strcpy(*p, "other"); name = NULL; id = -1; break; } *p += strlen(*p); *(*p)++ = ':'; if (name != NULL) { strcpy(*p, name); *p += strlen(*p); } else if (tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { append_id(p, id); id = -1; } *(*p)++ = ':'; *(*p)++ = (perm & 0444) ? 'r' : '-'; *(*p)++ = (perm & 0222) ? 'w' : '-'; *(*p)++ = (perm & 0111) ? 'x' : '-'; if (id != -1) { *(*p)++ = ':'; append_id(p, id); } **p = '\0'; } /* * Parse a textual ACL. This automatically recognizes and supports * extensions described above. The 'type' argument is used to * indicate the type that should be used for any entries not * explicitly marked as "default:". */ int archive_acl_parse_w(struct archive_acl *acl, const wchar_t *text, int default_type) { struct { const wchar_t *start; const wchar_t *end; } field[4], name; int fields, n; int type, tag, permset, id; wchar_t sep; while (text != NULL && *text != L'\0') { /* * Parse the fields out of the next entry, * advance 'text' to start of next entry. */ fields = 0; do { const wchar_t *start, *end; next_field_w(&text, &start, &end, &sep); if (fields < 4) { field[fields].start = start; field[fields].end = end; } ++fields; } while (sep == L':'); /* Set remaining fields to blank. */ for (n = fields; n < 4; ++n) field[n].start = field[n].end = NULL; /* Check for a numeric ID in field 1 or 3. */ id = -1; isint_w(field[1].start, field[1].end, &id); /* Field 3 is optional. */ if (id == -1 && fields > 3) isint_w(field[3].start, field[3].end, &id); /* * Solaris extension: "defaultuser::rwx" is the * default ACL corresponding to "user::rwx", etc. */ if (field[0].end - field[0].start > 7 && wmemcmp(field[0].start, L"default", 7) == 0) { type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; field[0].start += 7; } else type = default_type; name.start = name.end = NULL; if (prefix_w(field[0].start, field[0].end, L"user")) { if (!ismode_w(field[2].start, field[2].end, &permset)) return (ARCHIVE_WARN); if (id != -1 || field[1].start < field[1].end) { tag = ARCHIVE_ENTRY_ACL_USER; name = field[1]; } else tag = ARCHIVE_ENTRY_ACL_USER_OBJ; } else if (prefix_w(field[0].start, field[0].end, L"group")) { if (!ismode_w(field[2].start, field[2].end, &permset)) return (ARCHIVE_WARN); if (id != -1 || field[1].start < field[1].end) { tag = ARCHIVE_ENTRY_ACL_GROUP; name = field[1]; } else tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; } else if (prefix_w(field[0].start, field[0].end, L"other")) { if (fields == 2 && field[1].start < field[1].end && ismode_w(field[1].start, field[1].end, &permset)) { /* This is Solaris-style "other:rwx" */ } else if (fields == 3 && field[1].start == field[1].end && field[2].start < field[2].end && ismode_w(field[2].start, field[2].end, &permset)) { /* This is FreeBSD-style "other::rwx" */ } else return (ARCHIVE_WARN); tag = ARCHIVE_ENTRY_ACL_OTHER; } else if (prefix_w(field[0].start, field[0].end, L"mask")) { if (fields == 2 && field[1].start < field[1].end && ismode_w(field[1].start, field[1].end, &permset)) { /* This is Solaris-style "mask:rwx" */ } else if (fields == 3 && field[1].start == field[1].end && field[2].start < field[2].end && ismode_w(field[2].start, field[2].end, &permset)) { /* This is FreeBSD-style "mask::rwx" */ } else return (ARCHIVE_WARN); tag = ARCHIVE_ENTRY_ACL_MASK; } else return (ARCHIVE_WARN); /* Add entry to the internal list. */ archive_acl_add_entry_w_len(acl, type, permset, tag, id, name.start, name.end - name.start); } return (ARCHIVE_OK); } /* * Parse a string to a positive decimal integer. Returns true if * the string is non-empty and consists only of decimal digits, * false otherwise. */ static int isint_w(const wchar_t *start, const wchar_t *end, int *result) { int n = 0; if (start >= end) return (0); while (start < end) { if (*start < '0' || *start > '9') return (0); if (n > (INT_MAX / 10) || (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { n = INT_MAX; } else { n *= 10; n += *start - '0'; } start++; } *result = n; return (1); } /* * Parse a string as a mode field. Returns true if * the string is non-empty and consists only of mode characters, * false otherwise. */ static int ismode_w(const wchar_t *start, const wchar_t *end, int *permset) { const wchar_t *p; if (start >= end) return (0); p = start; *permset = 0; while (p < end) { switch (*p++) { case 'r': case 'R': *permset |= ARCHIVE_ENTRY_ACL_READ; break; case 'w': case 'W': *permset |= ARCHIVE_ENTRY_ACL_WRITE; break; case 'x': case 'X': *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; break; case '-': break; default: return (0); } } return (1); } /* * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated * to point to just after the separator. *start points to the first * character of the matched text and *end just after the last * character of the matched identifier. In particular *end - *start * is the length of the field body, not including leading or trailing * whitespace. */ static void next_field_w(const wchar_t **wp, const wchar_t **start, const wchar_t **end, wchar_t *sep) { /* Skip leading whitespace to find start of field. */ while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { (*wp)++; } *start = *wp; /* Scan for the separator. */ while (**wp != L'\0' && **wp != L',' && **wp != L':' && **wp != L'\n') { (*wp)++; } *sep = **wp; /* Trim trailing whitespace to locate end of field. */ *end = *wp - 1; while (**end == L' ' || **end == L'\t' || **end == L'\n') { (*end)--; } (*end)++; /* Adjust scanner location. */ if (**wp != L'\0') (*wp)++; } /* * Return true if the characters [start...end) are a prefix of 'test'. * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. */ static int prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test) { if (start == end) return (0); if (*start++ != *test++) return (0); while (start < end && *start++ == *test++) ; if (start < end) return (0); return (1); } /* * Parse a textual ACL. This automatically recognizes and supports * extensions described above. The 'type' argument is used to * indicate the type that should be used for any entries not * explicitly marked as "default:". */ int archive_acl_parse_l(struct archive_acl *acl, const char *text, int default_type, struct archive_string_conv *sc) { struct { const char *start; const char *end; } field[4], name; int fields, n, r, ret = ARCHIVE_OK; int type, tag, permset, id; char sep; while (text != NULL && *text != '\0') { /* * Parse the fields out of the next entry, * advance 'text' to start of next entry. */ fields = 0; do { const char *start, *end; next_field(&text, &start, &end, &sep); if (fields < 4) { field[fields].start = start; field[fields].end = end; } ++fields; } while (sep == ':'); /* Set remaining fields to blank. */ for (n = fields; n < 4; ++n) field[n].start = field[n].end = NULL; /* Check for a numeric ID in field 1 or 3. */ id = -1; isint(field[1].start, field[1].end, &id); /* Field 3 is optional. */ if (id == -1 && fields > 3) isint(field[3].start, field[3].end, &id); /* * Solaris extension: "defaultuser::rwx" is the * default ACL corresponding to "user::rwx", etc. */ if (field[0].end - field[0].start > 7 && memcmp(field[0].start, "default", 7) == 0) { type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; field[0].start += 7; } else type = default_type; name.start = name.end = NULL; if (prefix_c(field[0].start, field[0].end, "user")) { if (!ismode(field[2].start, field[2].end, &permset)) return (ARCHIVE_WARN); if (id != -1 || field[1].start < field[1].end) { tag = ARCHIVE_ENTRY_ACL_USER; name = field[1]; } else tag = ARCHIVE_ENTRY_ACL_USER_OBJ; } else if (prefix_c(field[0].start, field[0].end, "group")) { if (!ismode(field[2].start, field[2].end, &permset)) return (ARCHIVE_WARN); if (id != -1 || field[1].start < field[1].end) { tag = ARCHIVE_ENTRY_ACL_GROUP; name = field[1]; } else tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; } else if (prefix_c(field[0].start, field[0].end, "other")) { if (fields == 2 && field[1].start < field[1].end && ismode(field[1].start, field[1].end, &permset)) { /* This is Solaris-style "other:rwx" */ } else if (fields == 3 && field[1].start == field[1].end && field[2].start < field[2].end && ismode(field[2].start, field[2].end, &permset)) { /* This is FreeBSD-style "other::rwx" */ } else return (ARCHIVE_WARN); tag = ARCHIVE_ENTRY_ACL_OTHER; } else if (prefix_c(field[0].start, field[0].end, "mask")) { if (fields == 2 && field[1].start < field[1].end && ismode(field[1].start, field[1].end, &permset)) { /* This is Solaris-style "mask:rwx" */ } else if (fields == 3 && field[1].start == field[1].end && field[2].start < field[2].end && ismode(field[2].start, field[2].end, &permset)) { /* This is FreeBSD-style "mask::rwx" */ } else return (ARCHIVE_WARN); tag = ARCHIVE_ENTRY_ACL_MASK; } else return (ARCHIVE_WARN); /* Add entry to the internal list. */ r = archive_acl_add_entry_len_l(acl, type, permset, tag, id, name.start, name.end - name.start, sc); if (r < ARCHIVE_WARN) return (r); if (r != ARCHIVE_OK) ret = ARCHIVE_WARN; } return (ret); } /* * Parse a string to a positive decimal integer. Returns true if * the string is non-empty and consists only of decimal digits, * false otherwise. */ static int isint(const char *start, const char *end, int *result) { int n = 0; if (start >= end) return (0); while (start < end) { if (*start < '0' || *start > '9') return (0); if (n > (INT_MAX / 10) || (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { n = INT_MAX; } else { n *= 10; n += *start - '0'; } start++; } *result = n; return (1); } /* * Parse a string as a mode field. Returns true if * the string is non-empty and consists only of mode characters, * false otherwise. */ static int ismode(const char *start, const char *end, int *permset) { const char *p; if (start >= end) return (0); p = start; *permset = 0; while (p < end) { switch (*p++) { case 'r': case 'R': *permset |= ARCHIVE_ENTRY_ACL_READ; break; case 'w': case 'W': *permset |= ARCHIVE_ENTRY_ACL_WRITE; break; case 'x': case 'X': *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; break; case '-': break; default: return (0); } } return (1); } /* * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated * to point to just after the separator. *start points to the first * character of the matched text and *end just after the last * character of the matched identifier. In particular *end - *start * is the length of the field body, not including leading or trailing * whitespace. */ static void next_field(const char **p, const char **start, const char **end, char *sep) { /* Skip leading whitespace to find start of field. */ while (**p == ' ' || **p == '\t' || **p == '\n') { (*p)++; } *start = *p; /* Scan for the separator. */ while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') { (*p)++; } *sep = **p; /* Trim trailing whitespace to locate end of field. */ *end = *p - 1; while (**end == ' ' || **end == '\t' || **end == '\n') { (*end)--; } (*end)++; /* Adjust scanner location. */ if (**p != '\0') (*p)++; } /* * Return true if the characters [start...end) are a prefix of 'test'. * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. */ static int prefix_c(const char *start, const char *end, const char *test) { if (start == end) return (0); if (*start++ != *test++) return (0); while (start < end && *start++ == *test++) ; if (start < end) return (0); return (1); } Index: projects/clang390-import/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c =================================================================== --- projects/clang390-import/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c (revision 305430) +++ projects/clang390-import/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c (revision 305431) @@ -1,1264 +1,1303 @@ /*- * Copyright (c) 2003-2009 Tim Kientzle * Copyright (c) 2010-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); /* This is the tree-walking code for POSIX systems. */ #if !defined(_WIN32) || defined(__CYGWIN__) #ifdef HAVE_SYS_TYPES_H /* Mac OSX requires sys/types.h before sys/acl.h. */ #include #endif #ifdef HAVE_SYS_ACL_H #include #endif #ifdef HAVE_SYS_EXTATTR_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #if defined(HAVE_SYS_XATTR_H) #include #elif defined(HAVE_ATTR_XATTR_H) #include #endif #ifdef HAVE_SYS_EA_H #include #endif #ifdef HAVE_ACL_LIBACL_H #include #endif #ifdef HAVE_COPYFILE_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef HAVE_LINUX_FIEMAP_H #include #endif #ifdef HAVE_LINUX_FS_H #include #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #ifdef HAVE_PATHS_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_disk_private.h" #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif /* * Linux and FreeBSD plug this obvious hole in POSIX.1e in * different ways. */ #if HAVE_ACL_GET_PERM #define ACL_GET_PERM acl_get_perm #elif HAVE_ACL_GET_PERM_NP #define ACL_GET_PERM acl_get_perm_np #endif static int setup_acls(struct archive_read_disk *, struct archive_entry *, int *fd); static int setup_mac_metadata(struct archive_read_disk *, struct archive_entry *, int *fd); static int setup_xattrs(struct archive_read_disk *, struct archive_entry *, int *fd); static int setup_sparse(struct archive_read_disk *, struct archive_entry *, int *fd); int archive_read_disk_entry_from_file(struct archive *_a, struct archive_entry *entry, int fd, const struct stat *st) { struct archive_read_disk *a = (struct archive_read_disk *)_a; const char *path, *name; struct stat s; int initial_fd = fd; int r, r1; archive_clear_error(_a); path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); if (a->tree == NULL) { if (st == NULL) { #if HAVE_FSTAT if (fd >= 0) { if (fstat(fd, &s) != 0) { archive_set_error(&a->archive, errno, "Can't fstat"); return (ARCHIVE_FAILED); } } else #endif #if HAVE_LSTAT if (!a->follow_symlinks) { if (lstat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't lstat %s", path); return (ARCHIVE_FAILED); } } else #endif if (stat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't stat %s", path); return (ARCHIVE_FAILED); } st = &s; } archive_entry_copy_stat(entry, st); } /* Lookup uname/gname */ name = archive_read_disk_uname(_a, archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(_a, archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); #ifdef HAVE_STRUCT_STAT_ST_FLAGS /* On FreeBSD, we get flags for free with the stat. */ /* TODO: Does this belong in copy_stat()? */ if (st->st_flags != 0) archive_entry_set_fflags(entry, st->st_flags, 0); #endif #if defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) /* Linux requires an extra ioctl to pull the flags. Although * this is an extra step, it has a nice side-effect: We get an * open file descriptor which we can use in the subsequent lookups. */ if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) { if (fd < 0) { if (a->tree != NULL) fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); else fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); } if (fd >= 0) { int stflags; r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags); if (r == 0 && stflags != 0) archive_entry_set_fflags(entry, stflags, 0); } } #endif #if defined(HAVE_READLINK) || defined(HAVE_READLINKAT) if (S_ISLNK(st->st_mode)) { size_t linkbuffer_len = st->st_size + 1; char *linkbuffer; int lnklen; linkbuffer = malloc(linkbuffer_len); if (linkbuffer == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't read link data"); return (ARCHIVE_FAILED); } if (a->tree != NULL) { #ifdef HAVE_READLINKAT lnklen = readlinkat(a->tree_current_dir_fd(a->tree), path, linkbuffer, linkbuffer_len); #else if (a->tree_enter_working_dir(a->tree) != 0) { archive_set_error(&a->archive, errno, "Couldn't read link data"); free(linkbuffer); return (ARCHIVE_FAILED); } lnklen = readlink(path, linkbuffer, linkbuffer_len); #endif /* HAVE_READLINKAT */ } else lnklen = readlink(path, linkbuffer, linkbuffer_len); if (lnklen < 0) { archive_set_error(&a->archive, errno, "Couldn't read link data"); free(linkbuffer); return (ARCHIVE_FAILED); } linkbuffer[lnklen] = 0; archive_entry_set_symlink(entry, linkbuffer); free(linkbuffer); } #endif /* HAVE_READLINK || HAVE_READLINKAT */ r = setup_acls(a, entry, &fd); if (!a->suppress_xattr) { r1 = setup_xattrs(a, entry, &fd); if (r1 < r) r = r1; } if (a->enable_copyfile) { r1 = setup_mac_metadata(a, entry, &fd); if (r1 < r) r = r1; } r1 = setup_sparse(a, entry, &fd); if (r1 < r) r = r1; /* If we opened the file earlier in this function, close it. */ if (initial_fd != fd) close(fd); return (r); } #if defined(__APPLE__) && defined(HAVE_COPYFILE_H) /* * The Mac OS "copyfile()" API copies the extended metadata for a * file into a separate file in AppleDouble format (see RFC 1740). * * Mac OS tar and cpio implementations store this extended * metadata as a separate entry just before the regular entry * with a "._" prefix added to the filename. * * Note that this is currently done unconditionally; the tar program has * an option to discard this information before the archive is written. * * TODO: If there's a failure, report it and return ARCHIVE_WARN. */ static int setup_mac_metadata(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { int tempfd = -1; int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR; struct stat copyfile_stat; int ret = ARCHIVE_OK; void *buff = NULL; int have_attrs; const char *name, *tempdir; struct archive_string tempfile; (void)fd; /* UNUSED */ name = archive_entry_sourcepath(entry); if (name == NULL) name = archive_entry_pathname(entry); if (name == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't open file to read extended attributes: No name"); return (ARCHIVE_WARN); } if (a->tree != NULL) { if (a->tree_enter_working_dir(a->tree) != 0) { archive_set_error(&a->archive, errno, "Couldn't change dir"); return (ARCHIVE_FAILED); } } /* Short-circuit if there's nothing to do. */ have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK); if (have_attrs == -1) { archive_set_error(&a->archive, errno, "Could not check extended attributes"); return (ARCHIVE_WARN); } if (have_attrs == 0) return (ARCHIVE_OK); tempdir = NULL; if (issetugid() == 0) tempdir = getenv("TMPDIR"); if (tempdir == NULL) tempdir = _PATH_TMP; archive_string_init(&tempfile); archive_strcpy(&tempfile, tempdir); archive_strcat(&tempfile, "tar.md.XXXXXX"); tempfd = mkstemp(tempfile.s); if (tempfd < 0) { archive_set_error(&a->archive, errno, "Could not open extended attribute file"); ret = ARCHIVE_WARN; goto cleanup; } __archive_ensure_cloexec_flag(tempfd); /* XXX I wish copyfile() could pack directly to a memory * buffer; that would avoid the temp file here. For that * matter, it would be nice if fcopyfile() actually worked, * that would reduce the many open/close races here. */ if (copyfile(name, tempfile.s, 0, copyfile_flags | COPYFILE_PACK)) { archive_set_error(&a->archive, errno, "Could not pack extended attributes"); ret = ARCHIVE_WARN; goto cleanup; } if (fstat(tempfd, ©file_stat)) { archive_set_error(&a->archive, errno, "Could not check size of extended attributes"); ret = ARCHIVE_WARN; goto cleanup; } buff = malloc(copyfile_stat.st_size); if (buff == NULL) { archive_set_error(&a->archive, errno, "Could not allocate memory for extended attributes"); ret = ARCHIVE_WARN; goto cleanup; } if (copyfile_stat.st_size != read(tempfd, buff, copyfile_stat.st_size)) { archive_set_error(&a->archive, errno, "Could not read extended attributes into memory"); ret = ARCHIVE_WARN; goto cleanup; } archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size); cleanup: if (tempfd >= 0) { close(tempfd); unlink(tempfile.s); } archive_string_free(&tempfile); free(buff); return (ret); } #else /* * Stub implementation for non-Mac systems. */ static int setup_mac_metadata(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif #ifdef HAVE_POSIX_ACL static int translate_acl(struct archive_read_disk *a, struct archive_entry *entry, acl_t acl, int archive_entry_acl_type); static int setup_acls(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { const char *accpath; acl_t acl; #if HAVE_ACL_IS_TRIVIAL_NP int r; #endif accpath = archive_entry_sourcepath(entry); if (accpath == NULL) accpath = archive_entry_pathname(entry); + if (*fd < 0 && a->tree != NULL) { + if (a->follow_symlinks || + archive_entry_filetype(entry) != AE_IFLNK) + *fd = a->open_on_current_dir(a->tree, + accpath, O_RDONLY | O_NONBLOCK); + if (*fd < 0) { + if (a->tree_enter_working_dir(a->tree) != 0) { + archive_set_error(&a->archive, errno, + "Couldn't access %s", accpath); + return (ARCHIVE_FAILED); + } + } + } + archive_entry_acl_clear(entry); + acl = NULL; + #ifdef ACL_TYPE_NFS4 /* Try NFS4 ACL first. */ if (*fd >= 0) +#if HAVE_ACL_GET_FD_NP + acl = acl_get_fd_np(*fd, ACL_TYPE_NFS4); +#else acl = acl_get_fd(*fd); +#endif #if HAVE_ACL_GET_LINK_NP else if (!a->follow_symlinks) acl = acl_get_link_np(accpath, ACL_TYPE_NFS4); #else else if ((!a->follow_symlinks) && (archive_entry_filetype(entry) == AE_IFLNK)) /* We can't get the ACL of a symlink, so we assume it can't have one. */ acl = NULL; #endif else acl = acl_get_file(accpath, ACL_TYPE_NFS4); + #if HAVE_ACL_IS_TRIVIAL_NP - /* Ignore "trivial" ACLs that just mirror the file mode. */ - acl_is_trivial_np(acl, &r); - if (r) { - acl_free(acl); - acl = NULL; + if (acl != NULL && acl_is_trivial_np(acl, &r) == 0) { + /* Ignore "trivial" ACLs that just mirror the file mode. */ + if (r) { + acl_free(acl); + acl = NULL; + /* + * Simultaneous NFSv4 and POSIX.1e ACLs for the same + * entry are not allowed, so we should return here + */ + return (ARCHIVE_OK); + } } #endif if (acl != NULL) { translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4); acl_free(acl); return (ARCHIVE_OK); } -#endif +#endif /* ACL_TYPE_NFS4 */ /* Retrieve access ACL from file. */ if (*fd >= 0) acl = acl_get_fd(*fd); #if HAVE_ACL_GET_LINK_NP else if (!a->follow_symlinks) acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS); #else else if ((!a->follow_symlinks) && (archive_entry_filetype(entry) == AE_IFLNK)) /* We can't get the ACL of a symlink, so we assume it can't have one. */ acl = NULL; #endif else acl = acl_get_file(accpath, ACL_TYPE_ACCESS); + +#if HAVE_ACL_IS_TRIVIAL_NP + /* Ignore "trivial" ACLs that just mirror the file mode. */ + if (acl != NULL && acl_is_trivial_np(acl, &r) == 0) { + if (r) { + acl_free(acl); + acl = NULL; + } + } +#endif + if (acl != NULL) { translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); acl_free(acl); + acl = NULL; } /* Only directories can have default ACLs. */ if (S_ISDIR(archive_entry_mode(entry))) { acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); if (acl != NULL) { translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); acl_free(acl); } } return (ARCHIVE_OK); } /* * Translate system ACL into libarchive internal structure. */ static struct { int archive_perm; int platform_perm; } acl_perm_map[] = { {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE}, {ARCHIVE_ENTRY_ACL_READ, ACL_READ}, #ifdef ACL_TYPE_NFS4 {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA}, {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY}, {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA}, {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE}, {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA}, {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY}, {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD}, {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES}, {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE}, {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL}, {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL}, {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER}, {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE} #endif }; #ifdef ACL_TYPE_NFS4 static struct { int archive_inherit; int platform_inherit; } acl_inherit_map[] = { {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT}, {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT}, {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT}, {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY} }; #endif static int translate_acl(struct archive_read_disk *a, struct archive_entry *entry, acl_t acl, int default_entry_acl_type) { acl_tag_t acl_tag; #ifdef ACL_TYPE_NFS4 acl_entry_type_t acl_type; acl_flagset_t acl_flagset; int brand, r; #endif acl_entry_t acl_entry; acl_permset_t acl_permset; int i, entry_acl_type; int s, ae_id, ae_tag, ae_perm; const char *ae_name; #ifdef ACL_TYPE_NFS4 // FreeBSD "brands" ACLs as POSIX.1e or NFSv4 // Make sure the "brand" on this ACL is consistent // with the default_entry_acl_type bits provided. acl_get_brand_np(acl, &brand); switch (brand) { case ACL_BRAND_POSIX: switch (default_entry_acl_type) { case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: break; default: // XXX set warning message? return ARCHIVE_FAILED; } break; case ACL_BRAND_NFS4: if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { // XXX set warning message? return ARCHIVE_FAILED; } break; default: // XXX set warning message? return ARCHIVE_FAILED; break; } #endif s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); while (s == 1) { ae_id = -1; ae_name = NULL; ae_perm = 0; acl_get_tag_type(acl_entry, &acl_tag); switch (acl_tag) { case ACL_USER: ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry); ae_name = archive_read_disk_uname(&a->archive, ae_id); ae_tag = ARCHIVE_ENTRY_ACL_USER; break; case ACL_GROUP: ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry); ae_name = archive_read_disk_gname(&a->archive, ae_id); ae_tag = ARCHIVE_ENTRY_ACL_GROUP; break; case ACL_MASK: ae_tag = ARCHIVE_ENTRY_ACL_MASK; break; case ACL_USER_OBJ: ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; break; case ACL_GROUP_OBJ: ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; break; case ACL_OTHER: ae_tag = ARCHIVE_ENTRY_ACL_OTHER; break; #ifdef ACL_TYPE_NFS4 case ACL_EVERYONE: ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE; break; #endif default: /* Skip types that libarchive can't support. */ s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); continue; } // XXX acl type maps to allow/deny/audit/YYYY bits // XXX acl_get_entry_type_np on FreeBSD returns EINVAL for // non-NFSv4 ACLs entry_acl_type = default_entry_acl_type; #ifdef ACL_TYPE_NFS4 r = acl_get_entry_type_np(acl_entry, &acl_type); if (r == 0) { switch (acl_type) { case ACL_ENTRY_TYPE_ALLOW: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; break; case ACL_ENTRY_TYPE_DENY: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY; break; case ACL_ENTRY_TYPE_AUDIT: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; break; case ACL_ENTRY_TYPE_ALARM: entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; break; } } /* * Libarchive stores "flag" (NFSv4 inheritance bits) * in the ae_perm bitmap. */ // XXX acl_get_flagset_np on FreeBSD returns EINVAL for // non-NFSv4 ACLs r = acl_get_flagset_np(acl_entry, &acl_flagset); if (r == 0) { for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { if (acl_get_flag_np(acl_flagset, acl_inherit_map[i].platform_inherit)) ae_perm |= acl_inherit_map[i].archive_inherit; } } #endif acl_get_permset(acl_entry, &acl_permset); for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { /* * acl_get_perm() is spelled differently on different * platforms; see above. */ if (ACL_GET_PERM(acl_permset, acl_perm_map[i].platform_perm)) ae_perm |= acl_perm_map[i].archive_perm; } archive_entry_acl_add_entry(entry, entry_acl_type, ae_perm, ae_tag, ae_id, ae_name); s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); } return (ARCHIVE_OK); } #else static int setup_acls(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif #if (HAVE_FGETXATTR && HAVE_FLISTXATTR && HAVE_LISTXATTR && \ HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR) || \ (HAVE_FGETEA && HAVE_FLISTEA && HAVE_LISTEA) /* * Linux and AIX extended attribute support. * * TODO: By using a stack-allocated buffer for the first * call to getxattr(), we might be able to avoid the second * call entirely. We only need the second call if the * stack-allocated buffer is too small. But a modest buffer * of 1024 bytes or so will often be big enough. Same applies * to listxattr(). */ static int setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, const char *name, int fd) { ssize_t size; void *value = NULL; const char *accpath; accpath = archive_entry_sourcepath(entry); if (accpath == NULL) accpath = archive_entry_pathname(entry); #if HAVE_FGETXATTR if (fd >= 0) size = fgetxattr(fd, name, NULL, 0); else if (!a->follow_symlinks) size = lgetxattr(accpath, name, NULL, 0); else size = getxattr(accpath, name, NULL, 0); #elif HAVE_FGETEA if (fd >= 0) size = fgetea(fd, name, NULL, 0); else if (!a->follow_symlinks) size = lgetea(accpath, name, NULL, 0); else size = getea(accpath, name, NULL, 0); #endif if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't query extended attribute"); return (ARCHIVE_WARN); } if (size > 0 && (value = malloc(size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } #if HAVE_FGETXATTR if (fd >= 0) size = fgetxattr(fd, name, value, size); else if (!a->follow_symlinks) size = lgetxattr(accpath, name, value, size); else size = getxattr(accpath, name, value, size); #elif HAVE_FGETEA if (fd >= 0) size = fgetea(fd, name, value, size); else if (!a->follow_symlinks) size = lgetea(accpath, name, value, size); else size = getea(accpath, name, value, size); #endif if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't read extended attribute"); return (ARCHIVE_WARN); } archive_entry_xattr_add_entry(entry, name, value, size); free(value); return (ARCHIVE_OK); } static int setup_xattrs(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { char *list, *p; const char *path; ssize_t list_size; path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); if (*fd < 0 && a->tree != NULL) { if (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK) *fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK); if (*fd < 0) { if (a->tree_enter_working_dir(a->tree) != 0) { archive_set_error(&a->archive, errno, "Couldn't access %s", path); return (ARCHIVE_FAILED); } } } #if HAVE_FLISTXATTR if (*fd >= 0) list_size = flistxattr(*fd, NULL, 0); else if (!a->follow_symlinks) list_size = llistxattr(path, NULL, 0); else list_size = listxattr(path, NULL, 0); #elif HAVE_FLISTEA if (*fd >= 0) list_size = flistea(*fd, NULL, 0); else if (!a->follow_symlinks) list_size = llistea(path, NULL, 0); else list_size = listea(path, NULL, 0); #endif if (list_size == -1) { if (errno == ENOTSUP || errno == ENOSYS) return (ARCHIVE_OK); archive_set_error(&a->archive, errno, "Couldn't list extended attributes"); return (ARCHIVE_WARN); } if (list_size == 0) return (ARCHIVE_OK); if ((list = malloc(list_size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } #if HAVE_FLISTXATTR if (*fd >= 0) list_size = flistxattr(*fd, list, list_size); else if (!a->follow_symlinks) list_size = llistxattr(path, list, list_size); else list_size = listxattr(path, list, list_size); #elif HAVE_FLISTEA if (*fd >= 0) list_size = flistea(*fd, list, list_size); else if (!a->follow_symlinks) list_size = llistea(path, list, list_size); else list_size = listea(path, list, list_size); #endif if (list_size == -1) { archive_set_error(&a->archive, errno, "Couldn't retrieve extended attributes"); free(list); return (ARCHIVE_WARN); } for (p = list; (p - list) < list_size; p += strlen(p) + 1) { if (strncmp(p, "system.", 7) == 0 || strncmp(p, "xfsroot.", 8) == 0) continue; setup_xattr(a, entry, p, *fd); } free(list); return (ARCHIVE_OK); } #elif HAVE_EXTATTR_GET_FILE && HAVE_EXTATTR_LIST_FILE && \ HAVE_DECL_EXTATTR_NAMESPACE_USER /* * FreeBSD extattr interface. */ /* TODO: Implement this. Follow the Linux model above, but * with FreeBSD-specific system calls, of course. Be careful * to not include the system extattrs that hold ACLs; we handle * those separately. */ static int setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, int namespace, const char *name, const char *fullname, int fd); static int setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, int namespace, const char *name, const char *fullname, int fd) { ssize_t size; void *value = NULL; const char *accpath; accpath = archive_entry_sourcepath(entry); if (accpath == NULL) accpath = archive_entry_pathname(entry); if (fd >= 0) size = extattr_get_fd(fd, namespace, name, NULL, 0); else if (!a->follow_symlinks) size = extattr_get_link(accpath, namespace, name, NULL, 0); else size = extattr_get_file(accpath, namespace, name, NULL, 0); if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't query extended attribute"); return (ARCHIVE_WARN); } if (size > 0 && (value = malloc(size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } if (fd >= 0) size = extattr_get_fd(fd, namespace, name, value, size); else if (!a->follow_symlinks) size = extattr_get_link(accpath, namespace, name, value, size); else size = extattr_get_file(accpath, namespace, name, value, size); if (size == -1) { free(value); archive_set_error(&a->archive, errno, "Couldn't read extended attribute"); return (ARCHIVE_WARN); } archive_entry_xattr_add_entry(entry, fullname, value, size); free(value); return (ARCHIVE_OK); } static int setup_xattrs(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { char buff[512]; char *list, *p; ssize_t list_size; const char *path; int namespace = EXTATTR_NAMESPACE_USER; path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); if (*fd < 0 && a->tree != NULL) { if (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK) *fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK); if (*fd < 0) { if (a->tree_enter_working_dir(a->tree) != 0) { archive_set_error(&a->archive, errno, "Couldn't access %s", path); return (ARCHIVE_FAILED); } } } if (*fd >= 0) list_size = extattr_list_fd(*fd, namespace, NULL, 0); else if (!a->follow_symlinks) list_size = extattr_list_link(path, namespace, NULL, 0); else list_size = extattr_list_file(path, namespace, NULL, 0); if (list_size == -1 && errno == EOPNOTSUPP) return (ARCHIVE_OK); if (list_size == -1) { archive_set_error(&a->archive, errno, "Couldn't list extended attributes"); return (ARCHIVE_WARN); } if (list_size == 0) return (ARCHIVE_OK); if ((list = malloc(list_size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } if (*fd >= 0) list_size = extattr_list_fd(*fd, namespace, list, list_size); else if (!a->follow_symlinks) list_size = extattr_list_link(path, namespace, list, list_size); else list_size = extattr_list_file(path, namespace, list, list_size); if (list_size == -1) { archive_set_error(&a->archive, errno, "Couldn't retrieve extended attributes"); free(list); return (ARCHIVE_WARN); } p = list; while ((p - list) < list_size) { size_t len = 255 & (int)*p; char *name; strcpy(buff, "user."); name = buff + strlen(buff); memcpy(name, p + 1, len); name[len] = '\0'; setup_xattr(a, entry, namespace, name, buff, *fd); p += 1 + len; } free(list); return (ARCHIVE_OK); } #else /* * Generic (stub) extended attribute support. */ static int setup_xattrs(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif #if defined(HAVE_LINUX_FIEMAP_H) /* * Linux sparse interface. * * The FIEMAP ioctl returns an "extent" for each physical allocation * on disk. We need to process those to generate a more compact list * of logical file blocks. We also need to be very careful to use * FIEMAP_FLAG_SYNC here, since there are reports that Linux sometimes * does not report allocations for newly-written data that hasn't * been synced to disk. * * It's important to return a minimal sparse file list because we want * to not trigger sparse file extensions if we don't have to, since * not all readers support them. */ static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { char buff[4096]; struct fiemap *fm; struct fiemap_extent *fe; int64_t size; int count, do_fiemap, iters; int exit_sts = ARCHIVE_OK; if (archive_entry_filetype(entry) != AE_IFREG || archive_entry_size(entry) <= 0 || archive_entry_hardlink(entry) != NULL) return (ARCHIVE_OK); if (*fd < 0) { const char *path; path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); if (a->tree != NULL) *fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); else *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (*fd < 0) { archive_set_error(&a->archive, errno, "Can't open `%s'", path); return (ARCHIVE_FAILED); } __archive_ensure_cloexec_flag(*fd); } /* Initialize buffer to avoid the error valgrind complains about. */ memset(buff, 0, sizeof(buff)); count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe); fm = (struct fiemap *)buff; fm->fm_start = 0; fm->fm_length = ~0ULL;; fm->fm_flags = FIEMAP_FLAG_SYNC; fm->fm_extent_count = count; do_fiemap = 1; size = archive_entry_size(entry); for (iters = 0; ; ++iters) { int i, r; r = ioctl(*fd, FS_IOC_FIEMAP, fm); if (r < 0) { /* When something error happens, it is better we * should return ARCHIVE_OK because an earlier * version(<2.6.28) cannot perfom FS_IOC_FIEMAP. */ goto exit_setup_sparse; } if (fm->fm_mapped_extents == 0) { if (iters == 0) { /* Fully sparse file; insert a zero-length "data" entry */ archive_entry_sparse_add_entry(entry, 0, 0); } break; } fe = fm->fm_extents; for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) { if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) { /* The fe_length of the last block does not * adjust itself to its size files. */ int64_t length = fe->fe_length; if (fe->fe_logical + length > (uint64_t)size) length -= fe->fe_logical + length - size; if (fe->fe_logical == 0 && length == size) { /* This is not sparse. */ do_fiemap = 0; break; } if (length > 0) archive_entry_sparse_add_entry(entry, fe->fe_logical, length); } if (fe->fe_flags & FIEMAP_EXTENT_LAST) do_fiemap = 0; } if (do_fiemap) { fe = fm->fm_extents + fm->fm_mapped_extents -1; fm->fm_start = fe->fe_logical + fe->fe_length; } else break; } exit_setup_sparse: return (exit_sts); } #elif defined(SEEK_HOLE) && defined(SEEK_DATA) && defined(_PC_MIN_HOLE_SIZE) /* * FreeBSD and Solaris sparse interface. */ static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { int64_t size; off_t initial_off; /* FreeBSD/Solaris only, so off_t okay here */ off_t off_s, off_e; /* FreeBSD/Solaris only, so off_t okay here */ int exit_sts = ARCHIVE_OK; int check_fully_sparse = 0; if (archive_entry_filetype(entry) != AE_IFREG || archive_entry_size(entry) <= 0 || archive_entry_hardlink(entry) != NULL) return (ARCHIVE_OK); /* Does filesystem support the reporting of hole ? */ if (*fd < 0 && a->tree != NULL) { const char *path; path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); *fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK); if (*fd < 0) { archive_set_error(&a->archive, errno, "Can't open `%s'", path); return (ARCHIVE_FAILED); } } if (*fd >= 0) { if (fpathconf(*fd, _PC_MIN_HOLE_SIZE) <= 0) return (ARCHIVE_OK); initial_off = lseek(*fd, 0, SEEK_CUR); if (initial_off != 0) lseek(*fd, 0, SEEK_SET); } else { const char *path; path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0) return (ARCHIVE_OK); *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (*fd < 0) { archive_set_error(&a->archive, errno, "Can't open `%s'", path); return (ARCHIVE_FAILED); } __archive_ensure_cloexec_flag(*fd); initial_off = 0; } off_s = 0; size = archive_entry_size(entry); while (off_s < size) { off_s = lseek(*fd, off_s, SEEK_DATA); if (off_s == (off_t)-1) { if (errno == ENXIO) { /* no more hole */ if (archive_entry_sparse_count(entry) == 0) { /* Potentially a fully-sparse file. */ check_fully_sparse = 1; } break; } archive_set_error(&a->archive, errno, "lseek(SEEK_HOLE) failed"); exit_sts = ARCHIVE_FAILED; goto exit_setup_sparse; } off_e = lseek(*fd, off_s, SEEK_HOLE); if (off_e == (off_t)-1) { if (errno == ENXIO) { off_e = lseek(*fd, 0, SEEK_END); if (off_e != (off_t)-1) break;/* no more data */ } archive_set_error(&a->archive, errno, "lseek(SEEK_DATA) failed"); exit_sts = ARCHIVE_FAILED; goto exit_setup_sparse; } if (off_s == 0 && off_e == size) break;/* This is not spase. */ archive_entry_sparse_add_entry(entry, off_s, off_e - off_s); off_s = off_e; } if (check_fully_sparse) { if (lseek(*fd, 0, SEEK_HOLE) == 0 && lseek(*fd, 0, SEEK_END) == size) { /* Fully sparse file; insert a zero-length "data" entry */ archive_entry_sparse_add_entry(entry, 0, 0); } } exit_setup_sparse: lseek(*fd, initial_off, SEEK_SET); return (exit_sts); } #else /* * Generic (stub) sparse support. */ static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { (void)a; /* UNUSED */ (void)entry; /* UNUSED */ (void)fd; /* UNUSED */ return (ARCHIVE_OK); } #endif #endif /* !defined(_WIN32) || defined(__CYGWIN__) */ Index: projects/clang390-import/contrib/libarchive/libarchive/archive_read_disk_posix.c =================================================================== --- projects/clang390-import/contrib/libarchive/libarchive/archive_read_disk_posix.c (revision 305430) +++ projects/clang390-import/contrib/libarchive/libarchive/archive_read_disk_posix.c (revision 305431) @@ -1,2664 +1,2673 @@ /*- * Copyright (c) 2003-2009 Tim Kientzle * Copyright (c) 2010-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* This is the tree-walking code for POSIX systems. */ #if !defined(_WIN32) || defined(__CYGWIN__) #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_STATFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_LINUX_MAGIC_H #include #endif #ifdef HAVE_LINUX_FS_H #include #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #ifdef HAVE_DIRECT_H #include #endif #ifdef HAVE_DIRENT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #include "archive.h" #include "archive_string.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_disk_private.h" #ifndef HAVE_FCHDIR #error fchdir function required. #endif #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif /*- * This is a new directory-walking system that addresses a number * of problems I've had with fts(3). In particular, it has no * pathname-length limits (other than the size of 'int'), handles * deep logical traversals, uses considerably less memory, and has * an opaque interface (easier to modify in the future). * * Internally, it keeps a single list of "tree_entry" items that * represent filesystem objects that require further attention. * Non-directories are not kept in memory: they are pulled from * readdir(), returned to the client, then freed as soon as possible. * Any directory entry to be traversed gets pushed onto the stack. * * There is surprisingly little information that needs to be kept for * each item on the stack. Just the name, depth (represented here as the * string length of the parent directory's pathname), and some markers * indicating how to get back to the parent (via chdir("..") for a * regular dir or via fchdir(2) for a symlink). */ /* * TODO: * 1) Loop checking. * 3) Arbitrary logical traversals by closing/reopening intermediate fds. */ struct restore_time { const char *name; time_t mtime; long mtime_nsec; time_t atime; long atime_nsec; mode_t filetype; int noatime; }; struct tree_entry { int depth; struct tree_entry *next; struct tree_entry *parent; struct archive_string name; size_t dirname_length; int64_t dev; int64_t ino; int flags; int filesystem_id; /* How to return back to the parent of a symlink. */ int symlink_parent_fd; /* How to restore time of a directory. */ struct restore_time restore_time; }; struct filesystem { int64_t dev; int synthetic; int remote; int noatime; #if defined(HAVE_READDIR_R) size_t name_max; #endif long incr_xfer_size; long max_xfer_size; long min_xfer_size; long xfer_align; /* * Buffer used for reading file contents. */ /* Exactly allocated memory pointer. */ unsigned char *allocation_ptr; /* Pointer adjusted to the filesystem alignment . */ unsigned char *buff; size_t buff_size; }; /* Definitions for tree_entry.flags bitmap. */ #define isDir 1 /* This entry is a regular directory. */ #define isDirLink 2 /* This entry is a symbolic link to a directory. */ #define needsFirstVisit 4 /* This is an initial entry. */ #define needsDescent 8 /* This entry needs to be previsited. */ #define needsOpen 16 /* This is a directory that needs to be opened. */ #define needsAscent 32 /* This entry needs to be postvisited. */ /* * Local data for this package. */ struct tree { struct tree_entry *stack; struct tree_entry *current; DIR *d; #define INVALID_DIR_HANDLE NULL struct dirent *de; #if defined(HAVE_READDIR_R) struct dirent *dirent; size_t dirent_allocated; #endif int flags; int visit_type; /* Error code from last failed operation. */ int tree_errno; /* Dynamically-sized buffer for holding path */ struct archive_string path; /* Last path element */ const char *basename; /* Leading dir length */ size_t dirname_length; int depth; int openCount; int maxOpenCount; int initial_dir_fd; int working_dir_fd; struct stat lst; struct stat st; int descend; int nlink; /* How to restore time of a file. */ struct restore_time restore_time; struct entry_sparse { int64_t length; int64_t offset; } *sparse_list, *current_sparse; int sparse_count; int sparse_list_size; char initial_symlink_mode; char symlink_mode; struct filesystem *current_filesystem; struct filesystem *filesystem_table; int initial_filesystem_id; int current_filesystem_id; int max_filesystem_id; int allocated_filesytem; int entry_fd; int entry_eof; int64_t entry_remaining_bytes; int64_t entry_total; unsigned char *entry_buff; size_t entry_buff_size; }; /* Definitions for tree.flags bitmap. */ #define hasStat 16 /* The st entry is valid. */ #define hasLstat 32 /* The lst entry is valid. */ #define onWorkingDir 64 /* We are on the working dir where we are * reading directory entry at this time. */ #define needsRestoreTimes 128 #define onInitialDir 256 /* We are on the initial dir. */ static int tree_dir_next_posix(struct tree *t); #ifdef HAVE_DIRENT_D_NAMLEN /* BSD extension; avoids need for a strlen() call. */ #define D_NAMELEN(dp) (dp)->d_namlen #else #define D_NAMELEN(dp) (strlen((dp)->d_name)) #endif /* Initiate/terminate a tree traversal. */ static struct tree *tree_open(const char *, int, int); static struct tree *tree_reopen(struct tree *, const char *, int); static void tree_close(struct tree *); static void tree_free(struct tree *); static void tree_push(struct tree *, const char *, int, int64_t, int64_t, struct restore_time *); static int tree_enter_initial_dir(struct tree *); static int tree_enter_working_dir(struct tree *); static int tree_current_dir_fd(struct tree *); /* * tree_next() returns Zero if there is no next entry, non-zero if * there is. Note that directories are visited three times. * Directories are always visited first as part of enumerating their * parent; that is a "regular" visit. If tree_descend() is invoked at * that time, the directory is added to a work list and will * subsequently be visited two more times: once just after descending * into the directory ("postdescent") and again just after ascending * back to the parent ("postascent"). * * TREE_ERROR_DIR is returned if the descent failed (because the * directory couldn't be opened, for instance). This is returned * instead of TREE_POSTDESCENT/TREE_POSTASCENT. TREE_ERROR_DIR is not a * fatal error, but it does imply that the relevant subtree won't be * visited. TREE_ERROR_FATAL is returned for an error that left the * traversal completely hosed. Right now, this is only returned for * chdir() failures during ascent. */ #define TREE_REGULAR 1 #define TREE_POSTDESCENT 2 #define TREE_POSTASCENT 3 #define TREE_ERROR_DIR -1 #define TREE_ERROR_FATAL -2 static int tree_next(struct tree *); /* * Return information about the current entry. */ /* * The current full pathname, length of the full pathname, and a name * that can be used to access the file. Because tree does use chdir * extensively, the access path is almost never the same as the full * current path. * * TODO: On platforms that support it, use openat()-style operations * to eliminate the chdir() operations entirely while still supporting * arbitrarily deep traversals. This makes access_path troublesome to * support, of course, which means we'll need a rich enough interface * that clients can function without it. (In particular, we'll need * tree_current_open() that returns an open file descriptor.) * */ static const char *tree_current_path(struct tree *); static const char *tree_current_access_path(struct tree *); /* * Request the lstat() or stat() data for the current path. Since the * tree package needs to do some of this anyway, and caches the * results, you should take advantage of it here if you need it rather * than make a redundant stat() or lstat() call of your own. */ static const struct stat *tree_current_stat(struct tree *); static const struct stat *tree_current_lstat(struct tree *); static int tree_current_is_symblic_link_target(struct tree *); /* The following functions use tricks to avoid a certain number of * stat()/lstat() calls. */ /* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */ static int tree_current_is_physical_dir(struct tree *); /* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */ static int tree_current_is_dir(struct tree *); static int update_current_filesystem(struct archive_read_disk *a, int64_t dev); static int setup_current_filesystem(struct archive_read_disk *); static int tree_target_is_same_as_parent(struct tree *, const struct stat *); static int _archive_read_disk_open(struct archive *, const char *); static int _archive_read_free(struct archive *); static int _archive_read_close(struct archive *); static int _archive_read_data_block(struct archive *, const void **, size_t *, int64_t *); static int _archive_read_next_header(struct archive *, struct archive_entry **); static int _archive_read_next_header2(struct archive *, struct archive_entry *); static const char *trivial_lookup_gname(void *, int64_t gid); static const char *trivial_lookup_uname(void *, int64_t uid); static int setup_sparse(struct archive_read_disk *, struct archive_entry *); static int close_and_restore_time(int fd, struct tree *, struct restore_time *); static int open_on_current_dir(struct tree *, const char *, int); static int tree_dup(int); static struct archive_vtable * archive_read_disk_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_free = _archive_read_free; av.archive_close = _archive_read_close; av.archive_read_data_block = _archive_read_data_block; av.archive_read_next_header = _archive_read_next_header; av.archive_read_next_header2 = _archive_read_next_header2; inited = 1; } return (&av); } const char * archive_read_disk_gname(struct archive *_a, int64_t gid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_gname")) return (NULL); if (a->lookup_gname == NULL) return (NULL); return ((*a->lookup_gname)(a->lookup_gname_data, gid)); } const char * archive_read_disk_uname(struct archive *_a, int64_t uid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_uname")) return (NULL); if (a->lookup_uname == NULL) return (NULL); return ((*a->lookup_uname)(a->lookup_uname_data, uid)); } int archive_read_disk_set_gname_lookup(struct archive *_a, void *private_data, const char * (*lookup_gname)(void *private, int64_t gid), void (*cleanup_gname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup"); if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) (a->cleanup_gname)(a->lookup_gname_data); a->lookup_gname = lookup_gname; a->cleanup_gname = cleanup_gname; a->lookup_gname_data = private_data; return (ARCHIVE_OK); } int archive_read_disk_set_uname_lookup(struct archive *_a, void *private_data, const char * (*lookup_uname)(void *private, int64_t uid), void (*cleanup_uname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup"); if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); a->lookup_uname = lookup_uname; a->cleanup_uname = cleanup_uname; a->lookup_uname_data = private_data; return (ARCHIVE_OK); } /* * Create a new archive_read_disk object and initialize it with global state. */ struct archive * archive_read_disk_new(void) { struct archive_read_disk *a; a = (struct archive_read_disk *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); a->archive.magic = ARCHIVE_READ_DISK_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->archive.vtable = archive_read_disk_vtable(); a->entry = archive_entry_new2(&a->archive); a->lookup_uname = trivial_lookup_uname; a->lookup_gname = trivial_lookup_gname; a->enable_copyfile = 1; a->traverse_mount_points = 1; a->open_on_current_dir = open_on_current_dir; a->tree_current_dir_fd = tree_current_dir_fd; a->tree_enter_working_dir = tree_enter_working_dir; return (&a->archive); } static int _archive_read_free(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; int r; if (_a == NULL) return (ARCHIVE_OK); archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free"); if (a->archive.state != ARCHIVE_STATE_CLOSED) r = _archive_read_close(&a->archive); else r = ARCHIVE_OK; tree_free(a->tree); if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) (a->cleanup_gname)(a->lookup_gname_data); if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); archive_string_free(&a->archive.error_string); archive_entry_free(a->entry); a->archive.magic = 0; __archive_clean(&a->archive); free(a); return (r); } static int _archive_read_close(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close"); if (a->archive.state != ARCHIVE_STATE_FATAL) a->archive.state = ARCHIVE_STATE_CLOSED; tree_close(a->tree); return (ARCHIVE_OK); } static void setup_symlink_mode(struct archive_read_disk *a, char symlink_mode, int follow_symlinks) { a->symlink_mode = symlink_mode; a->follow_symlinks = follow_symlinks; if (a->tree != NULL) { a->tree->initial_symlink_mode = a->symlink_mode; a->tree->symlink_mode = a->symlink_mode; } } int archive_read_disk_set_symlink_logical(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_logical"); setup_symlink_mode(a, 'L', 1); return (ARCHIVE_OK); } int archive_read_disk_set_symlink_physical(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_physical"); setup_symlink_mode(a, 'P', 0); return (ARCHIVE_OK); } int archive_read_disk_set_symlink_hybrid(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_hybrid"); setup_symlink_mode(a, 'H', 1);/* Follow symlinks initially. */ return (ARCHIVE_OK); } int archive_read_disk_set_atime_restored(struct archive *_a) { #ifndef HAVE_UTIMES static int warning_done = 0; #endif struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime"); #ifdef HAVE_UTIMES a->restore_time = 1; if (a->tree != NULL) a->tree->flags |= needsRestoreTimes; return (ARCHIVE_OK); #else if (warning_done) /* Warning was already emitted; suppress further warnings. */ return (ARCHIVE_OK); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Cannot restore access time on this system"); warning_done = 1; return (ARCHIVE_WARN); #endif } int archive_read_disk_set_behavior(struct archive *_a, int flags) { struct archive_read_disk *a = (struct archive_read_disk *)_a; int r = ARCHIVE_OK; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump"); if (flags & ARCHIVE_READDISK_RESTORE_ATIME) r = archive_read_disk_set_atime_restored(_a); else { a->restore_time = 0; if (a->tree != NULL) a->tree->flags &= ~needsRestoreTimes; } if (flags & ARCHIVE_READDISK_HONOR_NODUMP) a->honor_nodump = 1; else a->honor_nodump = 0; if (flags & ARCHIVE_READDISK_MAC_COPYFILE) a->enable_copyfile = 1; else a->enable_copyfile = 0; if (flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) a->traverse_mount_points = 0; else a->traverse_mount_points = 1; if (flags & ARCHIVE_READDISK_NO_XATTR) a->suppress_xattr = 1; else a->suppress_xattr = 0; return (r); } /* * Trivial implementations of gname/uname lookup functions. * These are normally overridden by the client, but these stub * versions ensure that we always have something that works. */ static const char * trivial_lookup_gname(void *private_data, int64_t gid) { (void)private_data; /* UNUSED */ (void)gid; /* UNUSED */ return (NULL); } static const char * trivial_lookup_uname(void *private_data, int64_t uid) { (void)private_data; /* UNUSED */ (void)uid; /* UNUSED */ return (NULL); } /* * Allocate memory for the reading buffer adjusted to the filesystem * alignment. */ static int setup_suitable_read_buffer(struct archive_read_disk *a) { struct tree *t = a->tree; struct filesystem *cf = t->current_filesystem; size_t asize; size_t s; if (cf->allocation_ptr == NULL) { /* If we couldn't get a filesystem alignment, * we use 4096 as default value but we won't use * O_DIRECT to open() and openat() operations. */ long xfer_align = (cf->xfer_align == -1)?4096:cf->xfer_align; if (cf->max_xfer_size != -1) asize = cf->max_xfer_size + xfer_align; else { long incr = cf->incr_xfer_size; /* Some platform does not set a proper value to * incr_xfer_size.*/ if (incr < 0) incr = cf->min_xfer_size; if (cf->min_xfer_size < 0) { incr = xfer_align; asize = xfer_align; } else asize = cf->min_xfer_size; /* Increase a buffer size up to 64K bytes in * a proper incremant size. */ while (asize < 1024*64) asize += incr; /* Take a margin to adjust to the filesystem * alignment. */ asize += xfer_align; } cf->allocation_ptr = malloc(asize); if (cf->allocation_ptr == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } /* * Calculate proper address for the filesystem. */ s = (uintptr_t)cf->allocation_ptr; s %= xfer_align; if (s > 0) s = xfer_align - s; /* * Set a read buffer pointer in the proper alignment of * the current filesystem. */ cf->buff = cf->allocation_ptr + s; cf->buff_size = asize - xfer_align; } return (ARCHIVE_OK); } static int _archive_read_data_block(struct archive *_a, const void **buff, size_t *size, int64_t *offset) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; int r; ssize_t bytes; size_t buffbytes; int empty_sparse_region = 0; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_block"); if (t->entry_eof || t->entry_remaining_bytes <= 0) { r = ARCHIVE_EOF; goto abort_read_data; } /* * Open the current file. */ if (t->entry_fd < 0) { int flags = O_RDONLY | O_BINARY | O_CLOEXEC; /* * Eliminate or reduce cache effects if we can. * * Carefully consider this to be enabled. */ #if defined(O_DIRECT) && 0/* Disabled for now */ if (t->current_filesystem->xfer_align != -1 && t->nlink == 1) flags |= O_DIRECT; #endif #if defined(O_NOATIME) /* * Linux has O_NOATIME flag; use it if we need. */ if ((t->flags & needsRestoreTimes) != 0 && t->restore_time.noatime == 0) flags |= O_NOATIME; do { #endif t->entry_fd = open_on_current_dir(t, tree_current_access_path(t), flags); __archive_ensure_cloexec_flag(t->entry_fd); #if defined(O_NOATIME) /* * When we did open the file with O_NOATIME flag, * if successful, set 1 to t->restore_time.noatime * not to restore an atime of the file later. * if failed by EPERM, retry it without O_NOATIME flag. */ if (flags & O_NOATIME) { if (t->entry_fd >= 0) t->restore_time.noatime = 1; else if (errno == EPERM) { flags &= ~O_NOATIME; continue; } } } while (0); #endif if (t->entry_fd < 0) { archive_set_error(&a->archive, errno, "Couldn't open %s", tree_current_path(t)); r = ARCHIVE_FAILED; tree_enter_initial_dir(t); goto abort_read_data; } tree_enter_initial_dir(t); } /* * Allocate read buffer if not allocated. */ if (t->current_filesystem->allocation_ptr == NULL) { r = setup_suitable_read_buffer(a); if (r != ARCHIVE_OK) { a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } } t->entry_buff = t->current_filesystem->buff; t->entry_buff_size = t->current_filesystem->buff_size; buffbytes = t->entry_buff_size; if ((int64_t)buffbytes > t->current_sparse->length) buffbytes = t->current_sparse->length; if (t->current_sparse->length == 0) empty_sparse_region = 1; /* * Skip hole. * TODO: Should we consider t->current_filesystem->xfer_align? */ if (t->current_sparse->offset > t->entry_total) { if (lseek(t->entry_fd, (off_t)t->current_sparse->offset, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek error"); r = ARCHIVE_FATAL; a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } bytes = t->current_sparse->offset - t->entry_total; t->entry_remaining_bytes -= bytes; t->entry_total += bytes; } /* * Read file contents. */ if (buffbytes > 0) { bytes = read(t->entry_fd, t->entry_buff, buffbytes); if (bytes < 0) { archive_set_error(&a->archive, errno, "Read error"); r = ARCHIVE_FATAL; a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } } else bytes = 0; /* * Return an EOF unless we've read a leading empty sparse region, which * is used to represent fully-sparse files. */ if (bytes == 0 && !empty_sparse_region) { /* Get EOF */ t->entry_eof = 1; r = ARCHIVE_EOF; goto abort_read_data; } *buff = t->entry_buff; *size = bytes; *offset = t->entry_total; t->entry_total += bytes; t->entry_remaining_bytes -= bytes; if (t->entry_remaining_bytes == 0) { /* Close the current file descriptor */ close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; t->entry_eof = 1; } t->current_sparse->offset += bytes; t->current_sparse->length -= bytes; if (t->current_sparse->length == 0 && !t->entry_eof) t->current_sparse++; return (ARCHIVE_OK); abort_read_data: *buff = NULL; *size = 0; *offset = t->entry_total; if (t->entry_fd >= 0) { /* Close the current file descriptor */ close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; } return (r); } static int next_entry(struct archive_read_disk *a, struct tree *t, struct archive_entry *entry) { const struct stat *st; /* info to use for this entry */ const struct stat *lst;/* lstat() information */ const char *name; int descend, r; st = NULL; lst = NULL; t->descend = 0; do { switch (tree_next(t)) { case TREE_ERROR_FATAL: archive_set_error(&a->archive, t->tree_errno, "%s: Unable to continue traversing directory tree", tree_current_path(t)); a->archive.state = ARCHIVE_STATE_FATAL; tree_enter_initial_dir(t); return (ARCHIVE_FATAL); case TREE_ERROR_DIR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: Couldn't visit directory", tree_current_path(t)); tree_enter_initial_dir(t); return (ARCHIVE_FAILED); case 0: tree_enter_initial_dir(t); return (ARCHIVE_EOF); case TREE_POSTDESCENT: case TREE_POSTASCENT: break; case TREE_REGULAR: lst = tree_current_lstat(t); if (lst == NULL) { archive_set_error(&a->archive, errno, "%s: Cannot stat", tree_current_path(t)); tree_enter_initial_dir(t); return (ARCHIVE_FAILED); } break; } } while (lst == NULL); #ifdef __APPLE__ if (a->enable_copyfile) { /* If we're using copyfile(), ignore "._XXX" files. */ const char *bname = strrchr(tree_current_path(t), '/'); if (bname == NULL) bname = tree_current_path(t); else ++bname; if (bname[0] == '.' && bname[1] == '_') return (ARCHIVE_RETRY); } #endif archive_entry_copy_pathname(entry, tree_current_path(t)); /* * Perform path matching. */ if (a->matching) { r = archive_match_path_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* * Distinguish 'L'/'P'/'H' symlink following. */ switch(t->symlink_mode) { case 'H': /* 'H': After the first item, rest like 'P'. */ t->symlink_mode = 'P'; /* 'H': First item (from command line) like 'L'. */ /* FALLTHROUGH */ case 'L': /* 'L': Do descend through a symlink to dir. */ descend = tree_current_is_dir(t); /* 'L': Follow symlinks to files. */ a->symlink_mode = 'L'; a->follow_symlinks = 1; /* 'L': Archive symlinks as targets, if we can. */ st = tree_current_stat(t); if (st != NULL && !tree_target_is_same_as_parent(t, st)) break; /* If stat fails, we have a broken symlink; * in that case, don't follow the link. */ /* FALLTHROUGH */ default: /* 'P': Don't descend through a symlink to dir. */ descend = tree_current_is_physical_dir(t); /* 'P': Don't follow symlinks to files. */ a->symlink_mode = 'P'; a->follow_symlinks = 0; /* 'P': Archive symlinks as symlinks. */ st = lst; break; } if (update_current_filesystem(a, st->st_dev) != ARCHIVE_OK) { a->archive.state = ARCHIVE_STATE_FATAL; tree_enter_initial_dir(t); return (ARCHIVE_FATAL); } if (t->initial_filesystem_id == -1) t->initial_filesystem_id = t->current_filesystem_id; if (!a->traverse_mount_points) { if (t->initial_filesystem_id != t->current_filesystem_id) descend = 0; } t->descend = descend; /* * Honor nodump flag. * If the file is marked with nodump flag, do not return this entry. */ if (a->honor_nodump) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) if (st->st_flags & UF_NODUMP) return (ARCHIVE_RETRY); #elif defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) &&\ defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) if (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) { int stflags; t->entry_fd = open_on_current_dir(t, tree_current_access_path(t), O_RDONLY | O_NONBLOCK | O_CLOEXEC); __archive_ensure_cloexec_flag(t->entry_fd); if (t->entry_fd >= 0) { r = ioctl(t->entry_fd, EXT2_IOC_GETFLAGS, &stflags); if (r == 0 && (stflags & EXT2_NODUMP_FL) != 0) return (ARCHIVE_RETRY); } } #endif } archive_entry_copy_stat(entry, st); /* Save the times to be restored. This must be in before * calling archive_read_disk_descend() or any chance of it, * especially, invokng a callback. */ t->restore_time.mtime = archive_entry_mtime(entry); t->restore_time.mtime_nsec = archive_entry_mtime_nsec(entry); t->restore_time.atime = archive_entry_atime(entry); t->restore_time.atime_nsec = archive_entry_atime_nsec(entry); t->restore_time.filetype = archive_entry_filetype(entry); t->restore_time.noatime = t->current_filesystem->noatime; /* * Perform time matching. */ if (a->matching) { r = archive_match_time_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* Lookup uname/gname */ name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); /* * Perform owner matching. */ if (a->matching) { r = archive_match_owner_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* * Invoke a meta data filter callback. */ if (a->metadata_filter_func) { if (!a->metadata_filter_func(&(a->archive), a->metadata_filter_data, entry)) return (ARCHIVE_RETRY); } /* * Populate the archive_entry with metadata from the disk. */ archive_entry_copy_sourcepath(entry, tree_current_access_path(t)); r = archive_read_disk_entry_from_file(&(a->archive), entry, t->entry_fd, st); return (r); } static int _archive_read_next_header(struct archive *_a, struct archive_entry **entryp) { int ret; struct archive_read_disk *a = (struct archive_read_disk *)_a; *entryp = NULL; ret = _archive_read_next_header2(_a, a->entry); *entryp = a->entry; return ret; } static int _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t; int r; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_next_header2"); t = a->tree; if (t->entry_fd >= 0) { close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; } for (;;) { r = next_entry(a, t, entry); if (t->entry_fd >= 0) { close(t->entry_fd); t->entry_fd = -1; } if (r == ARCHIVE_RETRY) { archive_entry_clear(entry); continue; } break; } /* Return to the initial directory. */ tree_enter_initial_dir(t); /* * EOF and FATAL are persistent at this layer. By * modifying the state, we guarantee that future calls to * read a header or read data will fail. */ switch (r) { case ARCHIVE_EOF: a->archive.state = ARCHIVE_STATE_EOF; break; case ARCHIVE_OK: case ARCHIVE_WARN: /* Overwrite the sourcepath based on the initial directory. */ archive_entry_copy_sourcepath(entry, tree_current_path(t)); t->entry_total = 0; if (archive_entry_filetype(entry) == AE_IFREG) { t->nlink = archive_entry_nlink(entry); t->entry_remaining_bytes = archive_entry_size(entry); t->entry_eof = (t->entry_remaining_bytes == 0)? 1: 0; if (!t->entry_eof && setup_sparse(a, entry) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { t->entry_remaining_bytes = 0; t->entry_eof = 1; } a->archive.state = ARCHIVE_STATE_DATA; break; case ARCHIVE_RETRY: break; case ARCHIVE_FATAL: a->archive.state = ARCHIVE_STATE_FATAL; break; } __archive_reset_read_data(&a->archive); return (r); } static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry) { struct tree *t = a->tree; int64_t length, offset; int i; t->sparse_count = archive_entry_sparse_reset(entry); if (t->sparse_count+1 > t->sparse_list_size) { free(t->sparse_list); t->sparse_list_size = t->sparse_count + 1; t->sparse_list = malloc(sizeof(t->sparse_list[0]) * t->sparse_list_size); if (t->sparse_list == NULL) { t->sparse_list_size = 0; archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } } for (i = 0; i < t->sparse_count; i++) { archive_entry_sparse_next(entry, &offset, &length); t->sparse_list[i].offset = offset; t->sparse_list[i].length = length; } if (i == 0) { t->sparse_list[i].offset = 0; t->sparse_list[i].length = archive_entry_size(entry); } else { t->sparse_list[i].offset = archive_entry_size(entry); t->sparse_list[i].length = 0; } t->current_sparse = t->sparse_list; return (ARCHIVE_OK); } int archive_read_disk_set_matching(struct archive *_a, struct archive *_ma, void (*_excluded_func)(struct archive *, void *, struct archive_entry *), void *_client_data) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_matching"); a->matching = _ma; a->excluded_cb_func = _excluded_func; a->excluded_cb_data = _client_data; return (ARCHIVE_OK); } int archive_read_disk_set_metadata_filter_callback(struct archive *_a, int (*_metadata_filter_func)(struct archive *, void *, struct archive_entry *), void *_client_data) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_metadata_filter_callback"); a->metadata_filter_func = _metadata_filter_func; a->metadata_filter_data = _client_data; return (ARCHIVE_OK); } int archive_read_disk_can_descend(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_disk_can_descend"); return (t->visit_type == TREE_REGULAR && t->descend); } /* * Called by the client to mark the directory just returned from * tree_next() as needing to be visited. */ int archive_read_disk_descend(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_disk_descend"); if (t->visit_type != TREE_REGULAR || !t->descend) return (ARCHIVE_OK); if (tree_current_is_physical_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, t->lst.st_dev, t->lst.st_ino, &t->restore_time); t->stack->flags |= isDir; } else if (tree_current_is_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, t->st.st_dev, t->st.st_ino, &t->restore_time); t->stack->flags |= isDirLink; } t->descend = 0; return (ARCHIVE_OK); } int archive_read_disk_open(struct archive *_a, const char *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED, "archive_read_disk_open"); archive_clear_error(&a->archive); return (_archive_read_disk_open(_a, pathname)); } int archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct archive_string path; int ret; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED, "archive_read_disk_open_w"); archive_clear_error(&a->archive); /* Make a char string from a wchar_t string. */ archive_string_init(&path); if (archive_string_append_from_wcs(&path, pathname, wcslen(pathname)) != 0) { if (errno == ENOMEM) archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't convert a path to a char string"); a->archive.state = ARCHIVE_STATE_FATAL; ret = ARCHIVE_FATAL; } else ret = _archive_read_disk_open(_a, path.s); archive_string_free(&path); return (ret); } static int _archive_read_disk_open(struct archive *_a, const char *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (a->tree != NULL) a->tree = tree_reopen(a->tree, pathname, a->restore_time); else a->tree = tree_open(pathname, a->symlink_mode, a->restore_time); if (a->tree == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } a->archive.state = ARCHIVE_STATE_HEADER; return (ARCHIVE_OK); } /* * Return a current filesystem ID which is index of the filesystem entry * you've visited through archive_read_disk. */ int archive_read_disk_current_filesystem(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem_id); } static int update_current_filesystem(struct archive_read_disk *a, int64_t dev) { struct tree *t = a->tree; int i, fid; if (t->current_filesystem != NULL && t->current_filesystem->dev == dev) return (ARCHIVE_OK); for (i = 0; i < t->max_filesystem_id; i++) { if (t->filesystem_table[i].dev == dev) { /* There is the filesytem ID we've already generated. */ t->current_filesystem_id = i; t->current_filesystem = &(t->filesystem_table[i]); return (ARCHIVE_OK); } } /* * This is the new filesytem which we have to generate a new ID for. */ fid = t->max_filesystem_id++; if (t->max_filesystem_id > t->allocated_filesytem) { size_t s; void *p; s = t->max_filesystem_id * 2; p = realloc(t->filesystem_table, s * sizeof(*t->filesystem_table)); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); return (ARCHIVE_FATAL); } t->filesystem_table = (struct filesystem *)p; t->allocated_filesytem = s; } t->current_filesystem_id = fid; t->current_filesystem = &(t->filesystem_table[fid]); t->current_filesystem->dev = dev; t->current_filesystem->allocation_ptr = NULL; t->current_filesystem->buff = NULL; /* Setup the current filesystem properties which depend on * platform specific. */ return (setup_current_filesystem(a)); } /* * Returns 1 if current filesystem is generated filesystem, 0 if it is not * or -1 if it is unknown. */ int archive_read_disk_current_filesystem_is_synthetic(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem->synthetic); } /* * Returns 1 if current filesystem is remote filesystem, 0 if it is not * or -1 if it is unknown. */ int archive_read_disk_current_filesystem_is_remote(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem->remote); } #if defined(_PC_REC_INCR_XFER_SIZE) && defined(_PC_REC_MAX_XFER_SIZE) &&\ defined(_PC_REC_MIN_XFER_SIZE) && defined(_PC_REC_XFER_ALIGN) static int get_xfer_size(struct tree *t, int fd, const char *path) { t->current_filesystem->xfer_align = -1; errno = 0; if (fd >= 0) { t->current_filesystem->incr_xfer_size = fpathconf(fd, _PC_REC_INCR_XFER_SIZE); t->current_filesystem->max_xfer_size = fpathconf(fd, _PC_REC_MAX_XFER_SIZE); t->current_filesystem->min_xfer_size = fpathconf(fd, _PC_REC_MIN_XFER_SIZE); t->current_filesystem->xfer_align = fpathconf(fd, _PC_REC_XFER_ALIGN); } else if (path != NULL) { t->current_filesystem->incr_xfer_size = pathconf(path, _PC_REC_INCR_XFER_SIZE); t->current_filesystem->max_xfer_size = pathconf(path, _PC_REC_MAX_XFER_SIZE); t->current_filesystem->min_xfer_size = pathconf(path, _PC_REC_MIN_XFER_SIZE); t->current_filesystem->xfer_align = pathconf(path, _PC_REC_XFER_ALIGN); } /* At least we need an alignment size. */ if (t->current_filesystem->xfer_align == -1) return ((errno == EINVAL)?1:-1); else return (0); } #else static int get_xfer_size(struct tree *t, int fd, const char *path) { (void)t; /* UNUSED */ (void)fd; /* UNUSED */ (void)path; /* UNUSED */ return (1);/* Not supported */ } #endif #if defined(HAVE_STATFS) && defined(HAVE_FSTATFS) && defined(MNT_LOCAL) \ && !defined(ST_LOCAL) /* * Gather current filesystem properties on FreeBSD, OpenBSD and Mac OS X. */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statfs sfs; #if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC) -# if defined(HAVE_STRUCT_VFSCONF) - struct vfsconf vfc; -# else +/* TODO: configure should set GETVFSBYNAME_ARG_TYPE to make + * this accurate; some platforms have both and we need the one that's + * used by getvfsbyname() + * + * Then the following would become: + * #if defined(GETVFSBYNAME_ARG_TYPE) + * GETVFSBYNAME_ARG_TYPE vfc; + * #endif + */ +# if defined(HAVE_STRUCT_XVFSCONF) struct xvfsconf vfc; +# else + struct vfsconf vfc; # endif #endif int r, xr = 0; #if !defined(HAVE_STRUCT_STATFS_F_NAMEMAX) long nm; #endif t->current_filesystem->synthetic = -1; t->current_filesystem->remote = -1; if (tree_current_is_symblic_link_target(t)) { #if defined(HAVE_OPENAT) /* * Get file system statistics on any directory * where current is. */ int fd = openat(tree_current_dir_fd(t), tree_current_access_path(t), O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); return (ARCHIVE_FAILED); } r = fstatfs(fd, &sfs); if (r == 0) xr = get_xfer_size(t, fd, NULL); close(fd); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } r = statfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); #endif } else { r = fstatfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); } if (r == -1 || xr == -1) { archive_set_error(&a->archive, errno, "statfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* pathconf(_PC_REX_*) operations are not supported. */ t->current_filesystem->xfer_align = sfs.f_bsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = sfs.f_iosize; t->current_filesystem->incr_xfer_size = sfs.f_iosize; } if (sfs.f_flags & MNT_LOCAL) t->current_filesystem->remote = 0; else t->current_filesystem->remote = 1; #if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC) r = getvfsbyname(sfs.f_fstypename, &vfc); if (r == -1) { archive_set_error(&a->archive, errno, "getvfsbyname failed"); return (ARCHIVE_FAILED); } if (vfc.vfc_flags & VFCF_SYNTHETIC) t->current_filesystem->synthetic = 1; else t->current_filesystem->synthetic = 0; #endif #if defined(MNT_NOATIME) if (sfs.f_flags & MNT_NOATIME) t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; #if defined(HAVE_READDIR_R) /* Set maximum filename length. */ #if defined(HAVE_STRUCT_STATFS_F_NAMEMAX) t->current_filesystem->name_max = sfs.f_namemax; #else # if defined(_PC_NAME_MAX) /* Mac OS X does not have f_namemax in struct statfs. */ if (tree_current_is_symblic_link_target(t)) { if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX); } else nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX); # else nm = -1; # endif if (nm == -1) t->current_filesystem->name_max = NAME_MAX; else t->current_filesystem->name_max = nm; #endif #endif /* HAVE_READDIR_R */ return (ARCHIVE_OK); } #elif (defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS)) && defined(ST_LOCAL) /* * Gather current filesystem properties on NetBSD */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statvfs sfs; int r, xr = 0; t->current_filesystem->synthetic = -1; if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } if (tree_current_is_symblic_link_target(t)) { r = statvfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); } else { #ifdef HAVE_FSTATVFS r = fstatvfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); #else r = statvfs(".", &sfs); if (r == 0) xr = get_xfer_size(t, -1, "."); #endif } if (r == -1 || xr == -1) { t->current_filesystem->remote = -1; archive_set_error(&a->archive, errno, "statvfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* Usuall come here unless NetBSD supports _PC_REC_XFER_ALIGN * for pathconf() function. */ t->current_filesystem->xfer_align = sfs.f_frsize; t->current_filesystem->max_xfer_size = -1; #if defined(HAVE_STRUCT_STATVFS_F_IOSIZE) t->current_filesystem->min_xfer_size = sfs.f_iosize; t->current_filesystem->incr_xfer_size = sfs.f_iosize; #else t->current_filesystem->min_xfer_size = sfs.f_bsize; t->current_filesystem->incr_xfer_size = sfs.f_bsize; #endif } if (sfs.f_flag & ST_LOCAL) t->current_filesystem->remote = 0; else t->current_filesystem->remote = 1; #if defined(ST_NOATIME) if (sfs.f_flag & ST_NOATIME) t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; /* Set maximum filename length. */ t->current_filesystem->name_max = sfs.f_namemax; return (ARCHIVE_OK); } #elif defined(HAVE_SYS_STATFS_H) && defined(HAVE_LINUX_MAGIC_H) &&\ defined(HAVE_STATFS) && defined(HAVE_FSTATFS) /* * Note: statfs is deprecated since LSB 3.2 */ #ifndef CIFS_SUPER_MAGIC #define CIFS_SUPER_MAGIC 0xFF534D42 #endif #ifndef DEVFS_SUPER_MAGIC #define DEVFS_SUPER_MAGIC 0x1373 #endif /* * Gather current filesystem properties on Linux */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statfs sfs; #if defined(HAVE_STATVFS) struct statvfs svfs; #endif int r, vr = 0, xr = 0; if (tree_current_is_symblic_link_target(t)) { #if defined(HAVE_OPENAT) /* * Get file system statistics on any directory * where current is. */ int fd = openat(tree_current_dir_fd(t), tree_current_access_path(t), O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); return (ARCHIVE_FAILED); } #if defined(HAVE_FSTATVFS) vr = fstatvfs(fd, &svfs);/* for f_flag, mount flags */ #endif r = fstatfs(fd, &sfs); if (r == 0) xr = get_xfer_size(t, fd, NULL); close(fd); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } #if defined(HAVE_STATVFS) vr = statvfs(tree_current_access_path(t), &svfs); #endif r = statfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); #endif } else { #ifdef HAVE_FSTATFS #if defined(HAVE_FSTATVFS) vr = fstatvfs(tree_current_dir_fd(t), &svfs); #endif r = fstatfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } #if defined(HAVE_STATVFS) vr = statvfs(".", &svfs); #endif r = statfs(".", &sfs); if (r == 0) xr = get_xfer_size(t, -1, "."); #endif } if (r == -1 || xr == -1 || vr == -1) { t->current_filesystem->synthetic = -1; t->current_filesystem->remote = -1; archive_set_error(&a->archive, errno, "statfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* pathconf(_PC_REX_*) operations are not supported. */ #if defined(HAVE_STATVFS) t->current_filesystem->xfer_align = svfs.f_frsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = svfs.f_bsize; t->current_filesystem->incr_xfer_size = svfs.f_bsize; #else t->current_filesystem->xfer_align = sfs.f_frsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = sfs.f_bsize; t->current_filesystem->incr_xfer_size = sfs.f_bsize; #endif } switch (sfs.f_type) { case AFS_SUPER_MAGIC: case CIFS_SUPER_MAGIC: case CODA_SUPER_MAGIC: case NCP_SUPER_MAGIC:/* NetWare */ case NFS_SUPER_MAGIC: case SMB_SUPER_MAGIC: t->current_filesystem->remote = 1; t->current_filesystem->synthetic = 0; break; case DEVFS_SUPER_MAGIC: case PROC_SUPER_MAGIC: case USBDEVICE_SUPER_MAGIC: t->current_filesystem->remote = 0; t->current_filesystem->synthetic = 1; break; default: t->current_filesystem->remote = 0; t->current_filesystem->synthetic = 0; break; } #if defined(ST_NOATIME) #if defined(HAVE_STATVFS) if (svfs.f_flag & ST_NOATIME) #else if (sfs.f_flag & ST_NOATIME) #endif t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; #if defined(HAVE_READDIR_R) /* Set maximum filename length. */ t->current_filesystem->name_max = sfs.f_namelen; #endif return (ARCHIVE_OK); } #elif defined(HAVE_SYS_STATVFS_H) &&\ (defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS)) /* * Gather current filesystem properties on other posix platform. */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statvfs sfs; int r, xr = 0; t->current_filesystem->synthetic = -1;/* Not supported */ t->current_filesystem->remote = -1;/* Not supported */ if (tree_current_is_symblic_link_target(t)) { #if defined(HAVE_OPENAT) /* * Get file system statistics on any directory * where current is. */ int fd = openat(tree_current_dir_fd(t), tree_current_access_path(t), O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); return (ARCHIVE_FAILED); } r = fstatvfs(fd, &sfs); if (r == 0) xr = get_xfer_size(t, fd, NULL); close(fd); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } r = statvfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); #endif } else { #ifdef HAVE_FSTATVFS r = fstatvfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } r = statvfs(".", &sfs); if (r == 0) xr = get_xfer_size(t, -1, "."); #endif } if (r == -1 || xr == -1) { t->current_filesystem->synthetic = -1; t->current_filesystem->remote = -1; archive_set_error(&a->archive, errno, "statvfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* pathconf(_PC_REX_*) operations are not supported. */ t->current_filesystem->xfer_align = sfs.f_frsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = sfs.f_bsize; t->current_filesystem->incr_xfer_size = sfs.f_bsize; } #if defined(ST_NOATIME) if (sfs.f_flag & ST_NOATIME) t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; #if defined(HAVE_READDIR_R) /* Set maximum filename length. */ t->current_filesystem->name_max = sfs.f_namemax; #endif return (ARCHIVE_OK); } #else /* * Generic: Gather current filesystem properties. * TODO: Is this generic function really needed? */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; #if defined(_PC_NAME_MAX) && defined(HAVE_READDIR_R) long nm; #endif t->current_filesystem->synthetic = -1;/* Not supported */ t->current_filesystem->remote = -1;/* Not supported */ t->current_filesystem->noatime = 0; (void)get_xfer_size(t, -1, ".");/* Dummy call to avoid build error. */ t->current_filesystem->xfer_align = -1;/* Unknown */ t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = -1; t->current_filesystem->incr_xfer_size = -1; #if defined(HAVE_READDIR_R) /* Set maximum filename length. */ # if defined(_PC_NAME_MAX) if (tree_current_is_symblic_link_target(t)) { if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX); } else nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX); if (nm == -1) # endif /* _PC_NAME_MAX */ /* * Some sysmtes (HP-UX or others?) incorrectly defined * NAME_MAX macro to be a smaller value. */ # if defined(NAME_MAX) && NAME_MAX >= 255 t->current_filesystem->name_max = NAME_MAX; # else /* No way to get a trusted value of maximum filename * length. */ t->current_filesystem->name_max = PATH_MAX; # endif /* NAME_MAX */ # if defined(_PC_NAME_MAX) else t->current_filesystem->name_max = nm; # endif /* _PC_NAME_MAX */ #endif /* HAVE_READDIR_R */ return (ARCHIVE_OK); } #endif static int close_and_restore_time(int fd, struct tree *t, struct restore_time *rt) { #ifndef HAVE_UTIMES (void)t; /* UNUSED */ (void)rt; /* UNUSED */ return (close(fd)); #else #if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__) struct timespec timespecs[2]; #endif struct timeval times[2]; if ((t->flags & needsRestoreTimes) == 0 || rt->noatime) { if (fd >= 0) return (close(fd)); else return (0); } #if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__) timespecs[1].tv_sec = rt->mtime; timespecs[1].tv_nsec = rt->mtime_nsec; timespecs[0].tv_sec = rt->atime; timespecs[0].tv_nsec = rt->atime_nsec; /* futimens() is defined in POSIX.1-2008. */ if (futimens(fd, timespecs) == 0) return (close(fd)); #endif times[1].tv_sec = rt->mtime; times[1].tv_usec = rt->mtime_nsec / 1000; times[0].tv_sec = rt->atime; times[0].tv_usec = rt->atime_nsec / 1000; #if !defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES) && !defined(__CYGWIN__) if (futimes(fd, times) == 0) return (close(fd)); #endif close(fd); #if defined(HAVE_FUTIMESAT) if (futimesat(tree_current_dir_fd(t), rt->name, times) == 0) return (0); #endif #ifdef HAVE_LUTIMES if (lutimes(rt->name, times) != 0) #else if (AE_IFLNK != rt->filetype && utimes(rt->name, times) != 0) #endif return (-1); #endif return (0); } static int open_on_current_dir(struct tree *t, const char *path, int flags) { #ifdef HAVE_OPENAT return (openat(tree_current_dir_fd(t), path, flags)); #else if (tree_enter_working_dir(t) != 0) return (-1); return (open(path, flags)); #endif } static int tree_dup(int fd) { int new_fd; #ifdef F_DUPFD_CLOEXEC static volatile int can_dupfd_cloexec = 1; if (can_dupfd_cloexec) { new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (new_fd != -1) return (new_fd); /* Linux 2.6.18 - 2.6.23 declare F_DUPFD_CLOEXEC, * but it cannot be used. So we have to try dup(). */ /* We won't try F_DUPFD_CLOEXEC. */ can_dupfd_cloexec = 0; } #endif /* F_DUPFD_CLOEXEC */ new_fd = dup(fd); __archive_ensure_cloexec_flag(new_fd); return (new_fd); } /* * Add a directory path to the current stack. */ static void tree_push(struct tree *t, const char *path, int filesystem_id, int64_t dev, int64_t ino, struct restore_time *rt) { struct tree_entry *te; te = malloc(sizeof(*te)); memset(te, 0, sizeof(*te)); te->next = t->stack; te->parent = t->current; if (te->parent) te->depth = te->parent->depth + 1; t->stack = te; archive_string_init(&te->name); te->symlink_parent_fd = -1; archive_strcpy(&te->name, path); te->flags = needsDescent | needsOpen | needsAscent; te->filesystem_id = filesystem_id; te->dev = dev; te->ino = ino; te->dirname_length = t->dirname_length; te->restore_time.name = te->name.s; if (rt != NULL) { te->restore_time.mtime = rt->mtime; te->restore_time.mtime_nsec = rt->mtime_nsec; te->restore_time.atime = rt->atime; te->restore_time.atime_nsec = rt->atime_nsec; te->restore_time.filetype = rt->filetype; te->restore_time.noatime = rt->noatime; } } /* * Append a name to the current dir path. */ static void tree_append(struct tree *t, const char *name, size_t name_length) { size_t size_needed; t->path.s[t->dirname_length] = '\0'; t->path.length = t->dirname_length; /* Strip trailing '/' from name, unless entire name is "/". */ while (name_length > 1 && name[name_length - 1] == '/') name_length--; /* Resize pathname buffer as needed. */ size_needed = name_length + t->dirname_length + 2; archive_string_ensure(&t->path, size_needed); /* Add a separating '/' if it's needed. */ if (t->dirname_length > 0 && t->path.s[archive_strlen(&t->path)-1] != '/') archive_strappend_char(&t->path, '/'); t->basename = t->path.s + archive_strlen(&t->path); archive_strncat(&t->path, name, name_length); t->restore_time.name = t->basename; } /* * Open a directory tree for traversal. */ static struct tree * tree_open(const char *path, int symlink_mode, int restore_time) { struct tree *t; if ((t = malloc(sizeof(*t))) == NULL) return (NULL); memset(t, 0, sizeof(*t)); archive_string_init(&t->path); archive_string_ensure(&t->path, 31); t->initial_symlink_mode = symlink_mode; return (tree_reopen(t, path, restore_time)); } static struct tree * tree_reopen(struct tree *t, const char *path, int restore_time) { t->flags = (restore_time)?needsRestoreTimes:0; t->flags |= onInitialDir; t->visit_type = 0; t->tree_errno = 0; t->dirname_length = 0; t->depth = 0; t->descend = 0; t->current = NULL; t->d = INVALID_DIR_HANDLE; t->symlink_mode = t->initial_symlink_mode; archive_string_empty(&t->path); t->entry_fd = -1; t->entry_eof = 0; t->entry_remaining_bytes = 0; t->initial_filesystem_id = -1; /* First item is set up a lot like a symlink traversal. */ tree_push(t, path, 0, 0, 0, NULL); t->stack->flags = needsFirstVisit; t->maxOpenCount = t->openCount = 1; t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(t->initial_dir_fd); t->working_dir_fd = tree_dup(t->initial_dir_fd); return (t); } static int tree_descent(struct tree *t) { int flag, new_fd, r = 0; t->dirname_length = archive_strlen(&t->path); flag = O_RDONLY | O_CLOEXEC; #if defined(O_DIRECTORY) flag |= O_DIRECTORY; #endif new_fd = open_on_current_dir(t, t->stack->name.s, flag); __archive_ensure_cloexec_flag(new_fd); if (new_fd < 0) { t->tree_errno = errno; r = TREE_ERROR_DIR; } else { t->depth++; /* If it is a link, set up fd for the ascent. */ if (t->stack->flags & isDirLink) { t->stack->symlink_parent_fd = t->working_dir_fd; t->openCount++; if (t->openCount > t->maxOpenCount) t->maxOpenCount = t->openCount; } else close(t->working_dir_fd); /* Renew the current working directory. */ t->working_dir_fd = new_fd; t->flags &= ~onWorkingDir; } return (r); } /* * We've finished a directory; ascend back to the parent. */ static int tree_ascend(struct tree *t) { struct tree_entry *te; int new_fd, r = 0, prev_dir_fd; te = t->stack; prev_dir_fd = t->working_dir_fd; if (te->flags & isDirLink) new_fd = te->symlink_parent_fd; else { new_fd = open_on_current_dir(t, "..", O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(new_fd); } if (new_fd < 0) { t->tree_errno = errno; r = TREE_ERROR_FATAL; } else { /* Renew the current working directory. */ t->working_dir_fd = new_fd; t->flags &= ~onWorkingDir; /* Current directory has been changed, we should * close an fd of previous working directory. */ close_and_restore_time(prev_dir_fd, t, &te->restore_time); if (te->flags & isDirLink) { t->openCount--; te->symlink_parent_fd = -1; } t->depth--; } return (r); } /* * Return to the initial directory where tree_open() was performed. */ static int tree_enter_initial_dir(struct tree *t) { int r = 0; if ((t->flags & onInitialDir) == 0) { r = fchdir(t->initial_dir_fd); if (r == 0) { t->flags &= ~onWorkingDir; t->flags |= onInitialDir; } } return (r); } /* * Restore working directory of directory traversals. */ static int tree_enter_working_dir(struct tree *t) { int r = 0; /* * Change the current directory if really needed. * Sometimes this is unneeded when we did not do * descent. */ if (t->depth > 0 && (t->flags & onWorkingDir) == 0) { r = fchdir(t->working_dir_fd); if (r == 0) { t->flags &= ~onInitialDir; t->flags |= onWorkingDir; } } return (r); } static int tree_current_dir_fd(struct tree *t) { return (t->working_dir_fd); } /* * Pop the working stack. */ static void tree_pop(struct tree *t) { struct tree_entry *te; t->path.s[t->dirname_length] = '\0'; t->path.length = t->dirname_length; if (t->stack == t->current && t->current != NULL) t->current = t->current->parent; te = t->stack; t->stack = te->next; t->dirname_length = te->dirname_length; t->basename = t->path.s + t->dirname_length; while (t->basename[0] == '/') t->basename++; archive_string_free(&te->name); free(te); } /* * Get the next item in the tree traversal. */ static int tree_next(struct tree *t) { int r; while (t->stack != NULL) { /* If there's an open dir, get the next entry from there. */ if (t->d != INVALID_DIR_HANDLE) { r = tree_dir_next_posix(t); if (r == 0) continue; return (r); } if (t->stack->flags & needsFirstVisit) { /* Top stack item needs a regular visit. */ t->current = t->stack; tree_append(t, t->stack->name.s, archive_strlen(&(t->stack->name))); /* t->dirname_length = t->path_length; */ /* tree_pop(t); */ t->stack->flags &= ~needsFirstVisit; return (t->visit_type = TREE_REGULAR); } else if (t->stack->flags & needsDescent) { /* Top stack item is dir to descend into. */ t->current = t->stack; tree_append(t, t->stack->name.s, archive_strlen(&(t->stack->name))); t->stack->flags &= ~needsDescent; r = tree_descent(t); if (r != 0) { tree_pop(t); t->visit_type = r; } else t->visit_type = TREE_POSTDESCENT; return (t->visit_type); } else if (t->stack->flags & needsOpen) { t->stack->flags &= ~needsOpen; r = tree_dir_next_posix(t); if (r == 0) continue; return (r); } else if (t->stack->flags & needsAscent) { /* Top stack item is dir and we're done with it. */ r = tree_ascend(t); tree_pop(t); t->visit_type = r != 0 ? r : TREE_POSTASCENT; return (t->visit_type); } else { /* Top item on stack is dead. */ tree_pop(t); t->flags &= ~hasLstat; t->flags &= ~hasStat; } } return (t->visit_type = 0); } static int tree_dir_next_posix(struct tree *t) { int r; const char *name; size_t namelen; if (t->d == NULL) { #if defined(HAVE_READDIR_R) size_t dirent_size; #endif #if defined(HAVE_FDOPENDIR) t->d = fdopendir(tree_dup(t->working_dir_fd)); #else /* HAVE_FDOPENDIR */ if (tree_enter_working_dir(t) == 0) { t->d = opendir("."); #if HAVE_DIRFD || defined(dirfd) __archive_ensure_cloexec_flag(dirfd(t->d)); #endif } #endif /* HAVE_FDOPENDIR */ if (t->d == NULL) { r = tree_ascend(t); /* Undo "chdir" */ tree_pop(t); t->tree_errno = errno; t->visit_type = r != 0 ? r : TREE_ERROR_DIR; return (t->visit_type); } #if defined(HAVE_READDIR_R) dirent_size = offsetof(struct dirent, d_name) + t->filesystem_table[t->current->filesystem_id].name_max + 1; if (t->dirent == NULL || t->dirent_allocated < dirent_size) { free(t->dirent); t->dirent = malloc(dirent_size); if (t->dirent == NULL) { closedir(t->d); t->d = INVALID_DIR_HANDLE; (void)tree_ascend(t); tree_pop(t); t->tree_errno = ENOMEM; t->visit_type = TREE_ERROR_DIR; return (t->visit_type); } t->dirent_allocated = dirent_size; } #endif /* HAVE_READDIR_R */ } for (;;) { errno = 0; #if defined(HAVE_READDIR_R) r = readdir_r(t->d, t->dirent, &t->de); #ifdef _AIX /* Note: According to the man page, return value 9 indicates * that the readdir_r was not successful and the error code * is set to the global errno variable. And then if the end * of directory entries was reached, the return value is 9 * and the third parameter is set to NULL and errno is * unchanged. */ if (r == 9) r = errno; #endif /* _AIX */ if (r != 0 || t->de == NULL) { #else t->de = readdir(t->d); if (t->de == NULL) { r = errno; #endif closedir(t->d); t->d = INVALID_DIR_HANDLE; if (r != 0) { t->tree_errno = r; t->visit_type = TREE_ERROR_DIR; return (t->visit_type); } else return (0); } name = t->de->d_name; namelen = D_NAMELEN(t->de); t->flags &= ~hasLstat; t->flags &= ~hasStat; if (name[0] == '.' && name[1] == '\0') continue; if (name[0] == '.' && name[1] == '.' && name[2] == '\0') continue; tree_append(t, name, namelen); return (t->visit_type = TREE_REGULAR); } } /* * Get the stat() data for the entry just returned from tree_next(). */ static const struct stat * tree_current_stat(struct tree *t) { if (!(t->flags & hasStat)) { #ifdef HAVE_FSTATAT if (fstatat(tree_current_dir_fd(t), tree_current_access_path(t), &t->st, 0) != 0) #else if (tree_enter_working_dir(t) != 0) return NULL; if (stat(tree_current_access_path(t), &t->st) != 0) #endif return NULL; t->flags |= hasStat; } return (&t->st); } /* * Get the lstat() data for the entry just returned from tree_next(). */ static const struct stat * tree_current_lstat(struct tree *t) { if (!(t->flags & hasLstat)) { #ifdef HAVE_FSTATAT if (fstatat(tree_current_dir_fd(t), tree_current_access_path(t), &t->lst, AT_SYMLINK_NOFOLLOW) != 0) #else if (tree_enter_working_dir(t) != 0) return NULL; if (lstat(tree_current_access_path(t), &t->lst) != 0) #endif return NULL; t->flags |= hasLstat; } return (&t->lst); } /* * Test whether current entry is a dir or link to a dir. */ static int tree_current_is_dir(struct tree *t) { const struct stat *st; /* * If we already have lstat() info, then try some * cheap tests to determine if this is a dir. */ if (t->flags & hasLstat) { /* If lstat() says it's a dir, it must be a dir. */ st = tree_current_lstat(t); if (st == NULL) return 0; if (S_ISDIR(st->st_mode)) return 1; /* Not a dir; might be a link to a dir. */ /* If it's not a link, then it's not a link to a dir. */ if (!S_ISLNK(st->st_mode)) return 0; /* * It's a link, but we don't know what it's a link to, * so we'll have to use stat(). */ } st = tree_current_stat(t); /* If we can't stat it, it's not a dir. */ if (st == NULL) return 0; /* Use the definitive test. Hopefully this is cached. */ return (S_ISDIR(st->st_mode)); } /* * Test whether current entry is a physical directory. Usually, we * already have at least one of stat() or lstat() in memory, so we * use tricks to try to avoid an extra trip to the disk. */ static int tree_current_is_physical_dir(struct tree *t) { const struct stat *st; /* * If stat() says it isn't a dir, then it's not a dir. * If stat() data is cached, this check is free, so do it first. */ if (t->flags & hasStat) { st = tree_current_stat(t); if (st == NULL) return (0); if (!S_ISDIR(st->st_mode)) return (0); } /* * Either stat() said it was a dir (in which case, we have * to determine whether it's really a link to a dir) or * stat() info wasn't available. So we use lstat(), which * hopefully is already cached. */ st = tree_current_lstat(t); /* If we can't stat it, it's not a dir. */ if (st == NULL) return 0; /* Use the definitive test. Hopefully this is cached. */ return (S_ISDIR(st->st_mode)); } /* * Test whether the same file has been in the tree as its parent. */ static int tree_target_is_same_as_parent(struct tree *t, const struct stat *st) { struct tree_entry *te; for (te = t->current->parent; te != NULL; te = te->parent) { if (te->dev == (int64_t)st->st_dev && te->ino == (int64_t)st->st_ino) return (1); } return (0); } /* * Test whether the current file is symbolic link target and * on the other filesystem. */ static int tree_current_is_symblic_link_target(struct tree *t) { static const struct stat *lst, *st; lst = tree_current_lstat(t); st = tree_current_stat(t); return (st != NULL && lst != NULL && (int64_t)st->st_dev == t->current_filesystem->dev && st->st_dev != lst->st_dev); } /* * Return the access path for the entry just returned from tree_next(). */ static const char * tree_current_access_path(struct tree *t) { return (t->basename); } /* * Return the full path for the entry just returned from tree_next(). */ static const char * tree_current_path(struct tree *t) { return (t->path.s); } /* * Terminate the traversal. */ static void tree_close(struct tree *t) { if (t == NULL) return; if (t->entry_fd >= 0) { close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; } /* Close the handle of readdir(). */ if (t->d != INVALID_DIR_HANDLE) { closedir(t->d); t->d = INVALID_DIR_HANDLE; } /* Release anything remaining in the stack. */ while (t->stack != NULL) { if (t->stack->flags & isDirLink) close(t->stack->symlink_parent_fd); tree_pop(t); } if (t->working_dir_fd >= 0) { close(t->working_dir_fd); t->working_dir_fd = -1; } if (t->initial_dir_fd >= 0) { close(t->initial_dir_fd); t->initial_dir_fd = -1; } } /* * Release any resources. */ static void tree_free(struct tree *t) { int i; if (t == NULL) return; archive_string_free(&t->path); #if defined(HAVE_READDIR_R) free(t->dirent); #endif free(t->sparse_list); for (i = 0; i < t->max_filesystem_id; i++) free(t->filesystem_table[i].allocation_ptr); free(t->filesystem_table); free(t); } #endif Index: projects/clang390-import/contrib/libarchive/libarchive/test/test_acl_freebsd_posix1e.c =================================================================== --- projects/clang390-import/contrib/libarchive/libarchive/test/test_acl_freebsd_posix1e.c (revision 305430) +++ projects/clang390-import/contrib/libarchive/libarchive/test/test_acl_freebsd_posix1e.c (revision 305431) @@ -1,265 +1,418 @@ /*- * Copyright (c) 2003-2008 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test.h" __FBSDID("$FreeBSD: head/lib/libarchive/test/test_acl_freebsd.c 189427 2009-03-06 04:21:23Z kientzle $"); #if defined(__FreeBSD__) && __FreeBSD__ > 4 #include struct myacl_t { int type; /* Type of ACL: "access" or "default" */ int permset; /* Permissions for this class of users. */ int tag; /* Owner, User, Owning group, group, other, etc. */ int qual; /* GID or UID of user/group, depending on tag. */ const char *name; /* Name of user/group, depending on tag. */ }; static struct myacl_t acls2[] = { { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 77, "user77" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0, ARCHIVE_ENTRY_ACL_USER, 78, "user78" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007, ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE, ARCHIVE_ENTRY_ACL_MASK, -1, "" }, { 0, 0, 0, 0, NULL } }; static void set_acls(struct archive_entry *ae, struct myacl_t *acls) { int i; archive_entry_acl_clear(ae); for (i = 0; acls[i].name != NULL; i++) { archive_entry_acl_add_entry(ae, acls[i].type, acls[i].permset, acls[i].tag, acls[i].qual, acls[i].name); } } static int -acl_match(acl_entry_t aclent, struct myacl_t *myacl) -{ - gid_t g, *gp; - uid_t u, *up; - acl_tag_t tag_type; - acl_permset_t opaque_ps; +acl_entry_get_perm(acl_entry_t aclent) { int permset = 0; + acl_permset_t opaque_ps; - acl_get_tag_type(aclent, &tag_type); - /* translate the silly opaque permset to a bitmap */ acl_get_permset(aclent, &opaque_ps); if (acl_get_perm_np(opaque_ps, ACL_EXECUTE)) permset |= ARCHIVE_ENTRY_ACL_EXECUTE; if (acl_get_perm_np(opaque_ps, ACL_WRITE)) permset |= ARCHIVE_ENTRY_ACL_WRITE; if (acl_get_perm_np(opaque_ps, ACL_READ)) permset |= ARCHIVE_ENTRY_ACL_READ; + return permset; +} - if (permset != myacl->permset) +#if 0 +static int +acl_get_specific_entry(acl_t acl, acl_tag_t requested_tag_type, int requested_tag) { + int entry_id = ACL_FIRST_ENTRY; + acl_entry_t acl_entry; + acl_tag_t acl_tag_type; + + while (1 == acl_get_entry(acl, entry_id, &acl_entry)) { + /* After the first time... */ + entry_id = ACL_NEXT_ENTRY; + + /* If this matches, return perm mask */ + acl_get_tag_type(acl_entry, &acl_tag_type); + if (acl_tag_type == requested_tag_type) { + switch (acl_tag_type) { + case ACL_USER_OBJ: + if ((uid_t)requested_tag == *(uid_t *)(acl_get_qualifier(acl_entry))) { + return acl_entry_get_perm(acl_entry); + } + break; + case ACL_GROUP_OBJ: + if ((gid_t)requested_tag == *(gid_t *)(acl_get_qualifier(acl_entry))) { + return acl_entry_get_perm(acl_entry); + } + break; + case ACL_USER: + case ACL_GROUP: + case ACL_OTHER: + return acl_entry_get_perm(acl_entry); + default: + failure("Unexpected ACL tag type"); + assert(0); + } + } + + + } + return -1; +} +#endif + +static int +acl_match(acl_entry_t aclent, struct myacl_t *myacl) +{ + gid_t g, *gp; + uid_t u, *up; + acl_tag_t tag_type; + + if (myacl->permset != acl_entry_get_perm(aclent)) return (0); + acl_get_tag_type(aclent, &tag_type); switch (tag_type) { case ACL_USER_OBJ: if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0); break; case ACL_USER: if (myacl->tag != ARCHIVE_ENTRY_ACL_USER) return (0); up = acl_get_qualifier(aclent); u = *up; acl_free(up); if ((uid_t)myacl->qual != u) return (0); break; case ACL_GROUP_OBJ: if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0); break; case ACL_GROUP: if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP) return (0); gp = acl_get_qualifier(aclent); g = *gp; acl_free(gp); if ((gid_t)myacl->qual != g) return (0); break; case ACL_MASK: if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0); break; case ACL_OTHER: if (myacl->tag != ARCHIVE_ENTRY_ACL_OTHER) return (0); break; } return (1); } static void compare_acls(acl_t acl, struct myacl_t *myacls) { int *marker; int entry_id = ACL_FIRST_ENTRY; int matched; int i, n; acl_entry_t acl_entry; /* Count ACL entries in myacls array and allocate an indirect array. */ for (n = 0; myacls[n].name != NULL; ++n) continue; if (n) { marker = malloc(sizeof(marker[0]) * n); if (marker == NULL) return; for (i = 0; i < n; i++) marker[i] = i; } else marker = NULL; /* * Iterate over acls in system acl object, try to match each * one with an item in the myacls array. */ while (1 == acl_get_entry(acl, entry_id, &acl_entry)) { /* After the first time... */ entry_id = ACL_NEXT_ENTRY; /* Search for a matching entry (tag and qualifier) */ for (i = 0, matched = 0; i < n && !matched; i++) { if (acl_match(acl_entry, &myacls[marker[i]])) { /* We found a match; remove it. */ marker[i] = marker[n - 1]; n--; matched = 1; } } /* TODO: Print out more details in this case. */ failure("ACL entry on file that shouldn't be there"); assert(matched == 1); } /* Dump entries in the myacls array that weren't in the system acl. */ for (i = 0; i < n; ++i) { failure(" ACL entry missing from file: " "type=%d,permset=%d,tag=%d,qual=%d,name=``%s''\n", myacls[marker[i]].type, myacls[marker[i]].permset, myacls[marker[i]].tag, myacls[marker[i]].qual, myacls[marker[i]].name); assert(0); /* Record this as a failure. */ } free(marker); } #endif /* * Verify ACL restore-to-disk. This test is FreeBSD-specific. */ -DEFINE_TEST(test_acl_freebsd_posix1e) +DEFINE_TEST(test_acl_freebsd_posix1e_restore) { #if !defined(__FreeBSD__) skipping("FreeBSD-specific ACL restore test"); #elif __FreeBSD__ < 5 skipping("ACL restore supported only on FreeBSD 5.0 and later"); #else struct stat st; struct archive *a; struct archive_entry *ae; int n, fd; acl_t acl; /* * First, do a quick manual set/read of ACL data to * verify that the local filesystem does support ACLs. * If it doesn't, we'll simply skip the remaining tests. */ acl = acl_from_text("u::rwx,u:1:rw,g::rwx,g:15:rx,o::rwx,m::rwx"); assert((void *)acl != NULL); /* Create a test file and try to set an ACL on it. */ fd = open("pretest", O_WRONLY | O_CREAT | O_EXCL, 0777); failure("Could not create test file?!"); if (!assert(fd >= 0)) { acl_free(acl); return; } n = acl_set_fd(fd, acl); acl_free(acl); if (n != 0 && errno == EOPNOTSUPP) { close(fd); skipping("ACL tests require that ACL support be enabled on the filesystem"); return; } if (n != 0 && errno == EINVAL) { close(fd); skipping("This filesystem does not support POSIX.1e ACLs"); return; } failure("acl_set_fd(): errno = %d (%s)", errno, strerror(errno)); assertEqualInt(0, n); close(fd); /* Create a write-to-disk object. */ assert(NULL != (a = archive_write_disk_new())); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL); /* Populate an archive entry with some metadata, including ACL info */ ae = archive_entry_new(); assert(ae != NULL); archive_entry_set_pathname(ae, "test0"); archive_entry_set_mtime(ae, 123456, 7890); archive_entry_set_size(ae, 0); set_acls(ae, acls2); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); archive_entry_free(ae); /* Close the archive. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); /* Verify the data on disk. */ assertEqualInt(0, stat("test0", &st)); assertEqualInt(st.st_mtime, 123456); acl = acl_get_file("test0", ACL_TYPE_ACCESS); assert(acl != (acl_t)NULL); compare_acls(acl, acls2); acl_free(acl); +#endif +} + +/* + * Verify ACL reaed-from-disk. This test is FreeBSD-specific. + */ +DEFINE_TEST(test_acl_freebsd_posix1e_read) +{ +#if !defined(__FreeBSD__) + skipping("FreeBSD-specific ACL read test"); +#elif __FreeBSD__ < 5 + skipping("ACL read supported only on FreeBSD 5.0 and later"); +#else + struct archive *a; + struct archive_entry *ae; + int n, fd; + const char *acl1_text, *acl2_text; + acl_t acl1, acl2; + + /* + * Manually construct a directory and two files with + * different ACLs. This also serves to verify that ACLs + * are supported on the local filesystem. + */ + + /* Create a test file f1 with acl1 */ + acl1_text = "user::rwx,group::rwx,other::rwx,user:1:rw-,group:15:r-x,mask::rwx"; + acl1 = acl_from_text(acl1_text); + assert((void *)acl1 != NULL); + fd = open("f1", O_WRONLY | O_CREAT | O_EXCL, 0777); + failure("Could not create test file?!"); + if (!assert(fd >= 0)) { + acl_free(acl1); + return; + } + n = acl_set_fd(fd, acl1); + acl_free(acl1); + if (n != 0 && errno == EOPNOTSUPP) { + close(fd); + skipping("ACL tests require that ACL support be enabled on the filesystem"); + return; + } + if (n != 0 && errno == EINVAL) { + close(fd); + skipping("This filesystem does not support POSIX.1e ACLs"); + return; + } + failure("acl_set_fd(): errno = %d (%s)", + errno, strerror(errno)); + assertEqualInt(0, n); + close(fd); + + assertMakeDir("d", 0700); + + /* + * Create file d/f1 with acl2 + * + * This differs from acl1 in the u:1: and g:15: permissions. + * + * This file deliberately has the same name but a different ACL. + * Github Issue #777 explains how libarchive's directory traversal + * did not always correctly enter directories before attempting + * to read ACLs, resulting in reading the ACL from a like-named + * file in the wrong directory. + */ + acl2_text = "user::rwx,group::rwx,other::---,user:1:r--,group:15:r--,mask::rwx"; + acl2 = acl_from_text(acl2_text); + assert((void *)acl2 != NULL); + fd = open("d/f1", O_WRONLY | O_CREAT | O_EXCL, 0777); + failure("Could not create test file?!"); + if (!assert(fd >= 0)) { + acl_free(acl2); + return; + } + n = acl_set_fd(fd, acl2); + acl_free(acl2); + if (n != 0 && errno == EOPNOTSUPP) { + close(fd); + skipping("ACL tests require that ACL support be enabled on the filesystem"); + return; + } + if (n != 0 && errno == EINVAL) { + close(fd); + skipping("This filesystem does not support POSIX.1e ACLs"); + return; + } + failure("acl_set_fd(): errno = %d (%s)", + errno, strerror(errno)); + assertEqualInt(0, n); + close(fd); + + /* Create a read-from-disk object. */ + assert(NULL != (a = archive_read_disk_new())); + assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, ".")); + assert(NULL != (ae = archive_entry_new())); + + /* Walk the dir until we see both of the files */ + while (ARCHIVE_OK == archive_read_next_header2(a, ae)) { + archive_read_disk_descend(a); + if (strcmp(archive_entry_pathname(ae), "./f1") == 0) { + assertEqualString(archive_entry_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS), acl1_text); + + } else if (strcmp(archive_entry_pathname(ae), "./d/f1") == 0) { + assertEqualString(archive_entry_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS), acl2_text); + } + } + + archive_free(a); #endif } Index: projects/clang390-import/contrib/libarchive/libarchive =================================================================== --- projects/clang390-import/contrib/libarchive/libarchive (revision 305430) +++ projects/clang390-import/contrib/libarchive/libarchive (revision 305431) Property changes on: projects/clang390-import/contrib/libarchive/libarchive ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,2 ## Merged /head/contrib/libarchive/libarchive:r305017-305430 Merged /vendor/libarchive/dist/libarchive:r305420 Index: projects/clang390-import/contrib/libarchive =================================================================== --- projects/clang390-import/contrib/libarchive (revision 305430) +++ projects/clang390-import/contrib/libarchive (revision 305431) Property changes on: projects/clang390-import/contrib/libarchive ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,2 ## Merged /head/contrib/libarchive:r305017-305430 Merged /vendor/libarchive/dist:r305420 Index: projects/clang390-import/contrib/top/commands.c =================================================================== --- projects/clang390-import/contrib/top/commands.c (revision 305430) +++ projects/clang390-import/contrib/top/commands.c (revision 305431) @@ -1,541 +1,542 @@ /* * Top users/processes display for Unix * Version 3 * * This program may be freely redistributed, * but this entire comment MUST remain intact. * * Copyright (c) 1984, 1989, William LeFebvre, Rice University * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University * * $FreeBSD$ */ /* * This file contains the routines that implement some of the interactive * mode commands. Note that some of the commands are implemented in-line * in "main". This is necessary because they change the global state of * "top" (i.e.: changing the number of processes to display). */ #include "os.h" #include #include #include #include #include #include #include "commands.h" #include "sigdesc.h" /* generated automatically */ #include "top.h" #include "boolean.h" #include "utils.h" #include "machine.h" extern int errno; extern char *copyright; /* imported from screen.c */ extern int overstrike; int err_compar(); char *err_string(); static int str_adderr(char *str, int len, int err); static int str_addarg(char *str, int len, char *arg, int first); /* * show_help() - display the help screen; invoked in response to * either 'h' or '?'. */ void show_help() { printf("Top version %s, %s\n", version_string(), copyright); fputs("\n\n\ A top users display for Unix\n\ \n\ These single-character commands are available:\n\ \n\ ^L - redraw screen\n\ q - quit\n\ h or ? - help; show this text\n", stdout); /* not all commands are availalbe with overstrike terminals */ if (overstrike) { fputs("\n\ Other commands are also available, but this terminal is not\n\ sophisticated enough to handle those commands gracefully.\n\n", stdout); } else { fputs("\ C - toggle the displaying of weighted CPU percentage\n\ d - change number of displays to show\n\ e - list errors generated by last \"kill\" or \"renice\" command\n\ H - toggle the displaying of threads\n\ i or I - toggle the displaying of idle processes\n\ j - toggle the displaying of jail ID\n\ J - display processes for only one jail (+ selects all jails)\n\ k - kill processes; send a signal to a list of processes\n\ m - toggle the display between 'cpu' and 'io' modes\n\ n or # - change number of processes to display\n", stdout); #ifdef ORDER if (displaymode == DISP_CPU) fputs("\ o - specify sort order (pri, size, res, cpu, time, threads, jid, pid)\n", stdout); else fputs("\ o - specify sort order (vcsw, ivcsw, read, write, fault, total, jid, pid)\n", stdout); #endif fputs("\ P - toggle the displaying of per-CPU statistics\n\ r - renice a process\n\ s - change number of seconds to delay between updates\n\ S - toggle the displaying of system processes\n\ a - toggle the displaying of process titles\n\ t - toggle the display of this process\n\ u - display processes for only one user (+ selects all users)\n\ +w - toggle the display of swap use for each process\n\ z - toggle the displaying of the system idle process\n\ \n\ \n", stdout); } } /* * Utility routines that help with some of the commands. */ char *next_field(str) register char *str; { if ((str = strchr(str, ' ')) == NULL) { return(NULL); } *str = '\0'; while (*++str == ' ') /* loop */; /* if there is nothing left of the string, return NULL */ /* This fix is dedicated to Greg Earle */ return(*str == '\0' ? NULL : str); } int scanint(str, intp) char *str; int *intp; { register int val = 0; register char ch; /* if there is nothing left of the string, flag it as an error */ /* This fix is dedicated to Greg Earle */ if (*str == '\0') { return(-1); } while ((ch = *str++) != '\0') { if (isdigit(ch)) { val = val * 10 + (ch - '0'); } else if (isspace(ch)) { break; } else { return(-1); } } *intp = val; return(0); } /* * Some of the commands make system calls that could generate errors. * These errors are collected up in an array of structures for later * contemplation and display. Such routines return a string containing an * error message, or NULL if no errors occurred. The next few routines are * for manipulating and displaying these errors. We need an upper limit on * the number of errors, so we arbitrarily choose 20. */ #define ERRMAX 20 struct errs /* structure for a system-call error */ { int errnum; /* value of errno (that is, the actual error) */ char *arg; /* argument that caused the error */ }; static struct errs errs[ERRMAX]; static int errcnt; static char *err_toomany = " too many errors occurred"; static char *err_listem = " Many errors occurred. Press `e' to display the list of errors."; /* These macros get used to reset and log the errors */ #define ERR_RESET errcnt = 0 #define ERROR(p, e) if (errcnt >= ERRMAX) \ { \ return(err_toomany); \ } \ else \ { \ errs[errcnt].arg = (p); \ errs[errcnt++].errnum = (e); \ } /* * err_string() - return an appropriate error string. This is what the * command will return for displaying. If no errors were logged, then * return NULL. The maximum length of the error string is defined by * "STRMAX". */ #define STRMAX 80 char *err_string() { register struct errs *errp; register int cnt = 0; register int first = Yes; register int currerr = -1; int stringlen; /* characters still available in "string" */ static char string[STRMAX]; /* if there are no errors, return NULL */ if (errcnt == 0) { return(NULL); } /* sort the errors */ qsort((char *)errs, errcnt, sizeof(struct errs), err_compar); /* need a space at the front of the error string */ string[0] = ' '; string[1] = '\0'; stringlen = STRMAX - 2; /* loop thru the sorted list, building an error string */ while (cnt < errcnt) { errp = &(errs[cnt++]); if (errp->errnum != currerr) { if (currerr != -1) { if ((stringlen = str_adderr(string, stringlen, currerr)) < 2) { return(err_listem); } (void) strcat(string, "; "); /* we know there's more */ } currerr = errp->errnum; first = Yes; } if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0) { return(err_listem); } first = No; } /* add final message */ stringlen = str_adderr(string, stringlen, currerr); /* return the error string */ return(stringlen == 0 ? err_listem : string); } /* * str_adderr(str, len, err) - add an explanation of error "err" to * the string "str". */ static int str_adderr(str, len, err) char *str; int len; int err; { register char *msg; register int msglen; msg = err == 0 ? "Not a number" : errmsg(err); msglen = strlen(msg) + 2; if (len <= msglen) { return(0); } (void) strcat(str, ": "); (void) strcat(str, msg); return(len - msglen); } /* * str_addarg(str, len, arg, first) - add the string argument "arg" to * the string "str". This is the first in the group when "first" * is set (indicating that a comma should NOT be added to the front). */ static int str_addarg(str, len, arg, first) char *str; int len; char *arg; int first; { register int arglen; arglen = strlen(arg); if (!first) { arglen += 2; } if (len <= arglen) { return(0); } if (!first) { (void) strcat(str, ", "); } (void) strcat(str, arg); return(len - arglen); } /* * err_compar(p1, p2) - comparison routine used by "qsort" * for sorting errors. */ int err_compar(p1, p2) register struct errs *p1, *p2; { register int result; if ((result = p1->errnum - p2->errnum) == 0) { return(strcmp(p1->arg, p2->arg)); } return(result); } /* * error_count() - return the number of errors currently logged. */ int error_count() { return(errcnt); } /* * show_errors() - display on stdout the current log of errors. */ void show_errors() { register int cnt = 0; register struct errs *errp = errs; printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s"); while (cnt++ < errcnt) { printf("%5s: %s\n", errp->arg, errp->errnum == 0 ? "Not a number" : errmsg(errp->errnum)); errp++; } } /* * kill_procs(str) - send signals to processes, much like the "kill" * command does; invoked in response to 'k'. */ char *kill_procs(str) char *str; { register char *nptr; int signum = SIGTERM; /* default */ int procnum; struct sigdesc *sigp; int uid; /* reset error array */ ERR_RESET; /* remember our uid */ uid = getuid(); /* skip over leading white space */ while (isspace(*str)) str++; if (str[0] == '-') { /* explicit signal specified */ if ((nptr = next_field(str)) == NULL) { return(" kill: no processes specified"); } if (isdigit(str[1])) { (void) scanint(str + 1, &signum); if (signum <= 0 || signum >= NSIG) { return(" invalid signal number"); } } else { /* translate the name into a number */ for (sigp = sigdesc; sigp->name != NULL; sigp++) { if (strcmp(sigp->name, str + 1) == 0) { signum = sigp->number; break; } } /* was it ever found */ if (sigp->name == NULL) { return(" bad signal name"); } } /* put the new pointer in place */ str = nptr; } /* loop thru the string, killing processes */ do { if (scanint(str, &procnum) == -1) { ERROR(str, 0); } else { /* check process owner if we're not root */ if (uid && (uid != proc_owner(procnum))) { ERROR(str, EACCES); } /* go in for the kill */ else if (kill(procnum, signum) == -1) { /* chalk up an error */ ERROR(str, errno); } } } while ((str = next_field(str)) != NULL); /* return appropriate error string */ return(err_string()); } /* * renice_procs(str) - change the "nice" of processes, much like the * "renice" command does; invoked in response to 'r'. */ char *renice_procs(str) char *str; { register char negate; int prio; int procnum; int uid; ERR_RESET; uid = getuid(); /* allow for negative priority values */ if ((negate = (*str == '-')) != 0) { /* move past the minus sign */ str++; } /* use procnum as a temporary holding place and get the number */ procnum = scanint(str, &prio); /* negate if necessary */ if (negate) { prio = -prio; } #if defined(PRIO_MIN) && defined(PRIO_MAX) /* check for validity */ if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX) { return(" bad priority value"); } #endif /* move to the first process number */ if ((str = next_field(str)) == NULL) { return(" no processes specified"); } /* loop thru the process numbers, renicing each one */ do { if (scanint(str, &procnum) == -1) { ERROR(str, 0); } /* check process owner if we're not root */ else if (uid && (uid != proc_owner(procnum))) { ERROR(str, EACCES); } else if (setpriority(PRIO_PROCESS, procnum, prio) == -1) { ERROR(str, errno); } } while ((str = next_field(str)) != NULL); /* return appropriate error string */ return(err_string()); } Index: projects/clang390-import/contrib/top/machine.h =================================================================== --- projects/clang390-import/contrib/top/machine.h (revision 305430) +++ projects/clang390-import/contrib/top/machine.h (revision 305431) @@ -1,91 +1,92 @@ /* * $FreeBSD$ */ /* * This file defines the interface between top and the machine-dependent * module. It is NOT machine dependent and should not need to be changed * for any specific machine. */ #ifndef MACHINE_H #define MACHINE_H #include "top.h" /* * the statics struct is filled in by machine_init */ struct statics { char **procstate_names; char **cpustate_names; char **memory_names; char **arc_names; char **swap_names; #ifdef ORDER char **order_names; #endif int ncpus; }; /* * the system_info struct is filled in by a machine dependent routine. */ #ifdef p_active /* uw7 define macro p_active */ #define P_ACTIVE p_pactive #else #define P_ACTIVE p_active #endif struct system_info { int last_pid; double load_avg[NUM_AVERAGES]; int p_total; int P_ACTIVE; /* number of procs considered "active" */ int *procstates; int *cpustates; int *memory; int *arc; int *swap; struct timeval boottime; int ncpus; }; /* cpu_states is an array of percentages * 10. For example, the (integer) value 105 is 10.5% (or .105). */ /* * the process_select struct tells get_process_info what processes we * are interested in seeing */ struct process_select { int idle; /* show idle processes */ int self; /* show self */ int system; /* show system processes */ int thread; /* show threads */ int uid; /* only this uid (unless uid == -1) */ int wcpu; /* show weighted cpu */ int jid; /* only this jid (unless jid == -1) */ int jail; /* show jail ID */ + int swap; /* show swap usage */ int kidle; /* show per-CPU idle threads */ char *command; /* only this command (unless == NULL) */ }; /* routines defined by the machine dependent module */ char *format_header(); char *format_next_process(); void toggle_pcpustats(void); void get_system_info(struct system_info *si); -int machine_init(struct statics *statics, char do_unames); -int proc_owner(int pid); +int machine_init(struct statics *statics, char do_unames); +int proc_owner(int pid); /* non-int routines typically used by the machine dependent module */ char *printable(); #endif /* MACHINE_H */ Index: projects/clang390-import/contrib/top/top.c =================================================================== --- projects/clang390-import/contrib/top/top.c (revision 305430) +++ projects/clang390-import/contrib/top/top.c (revision 305431) @@ -1,1220 +1,1235 @@ char *copyright = "Copyright (c) 1984 through 1996, William LeFebvre"; /* * Top users/processes display for Unix * Version 3 * * This program may be freely redistributed, * but this entire comment MUST remain intact. * * Copyright (c) 1984, 1989, William LeFebvre, Rice University * Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University * Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory * Copyright (c) 1996, William LeFebvre, Group sys Consulting * * $FreeBSD$ */ /* * See the file "Changes" for information on version-to-version changes. */ /* * This file contains "main" and other high-level routines. */ /* * The following preprocessor variables, when defined, are used to * distinguish between different Unix implementations: * * SIGHOLD - use SVR4 sighold function when defined * SIGRELSE - use SVR4 sigrelse function when defined * FD_SET - macros FD_SET and FD_ZERO are used when defined */ #include "os.h" #include #include #include #include #include #include #include #include /* includes specific to top */ #include "commands.h" #include "display.h" /* interface to display package */ #include "screen.h" /* interface to screen package */ #include "top.h" #include "top.local.h" #include "boolean.h" #include "machine.h" #include "utils.h" #include "username.h" /* Size of the stdio buffer given to stdout */ #define Buffersize 2048 /* The buffer that stdio will use */ char stdoutbuf[Buffersize]; /* build Signal masks */ #define Smask(s) (1 << ((s) - 1)) /* for getopt: */ extern int optind; extern char *optarg; /* imported from screen.c */ extern int overstrike; static int fmt_flags = 0; int pcpu_stats = No; /* signal handling routines */ sigret_t leave(); sigret_t tstop(); #ifdef SIGWINCH sigret_t winch(); #endif volatile sig_atomic_t leaveflag; volatile sig_atomic_t tstopflag; volatile sig_atomic_t winchflag; /* internal routines */ void quit(); /* values which need to be accessed by signal handlers */ static int max_topn; /* maximum displayable processes */ /* miscellaneous things */ struct process_select ps; char *myname = "top"; jmp_buf jmp_int; /* routines that don't return int */ char *username(); char *ctime(); char *kill_procs(); char *renice_procs(); #ifdef ORDER extern int (*compares[])(); #else extern int proc_compare(); extern int io_compare(); #endif time_t time(); caddr_t get_process_info(); /* different routines for displaying the user's identification */ /* (values assigned to get_userid) */ char *username(); char *itoa7(); /* pointers to display routines */ void (*d_loadave)() = i_loadave; void (*d_procstates)() = i_procstates; void (*d_cpustates)() = i_cpustates; void (*d_memory)() = i_memory; void (*d_arc)() = i_arc; void (*d_swap)() = i_swap; void (*d_message)() = i_message; void (*d_header)() = i_header; void (*d_process)() = i_process; void reset_display(void); int main(argc, argv) int argc; char *argv[]; { register int i; register int active_procs; register int change; struct system_info system_info; struct statics statics; caddr_t processes; static char tempbuf1[50]; static char tempbuf2[50]; int old_sigmask; /* only used for BSD-style signals */ int topn = Default_TOPN; int delay = Default_DELAY; int displays = 0; /* indicates unspecified */ int sel_ret = 0; time_t curr_time; char *(*get_userid)() = username; char *uname_field = "USERNAME"; char *header_text; char *env_top; char **preset_argv; int preset_argc = 0; char **av; int ac; char dostates = No; char do_unames = Yes; char interactive = Maybe; char warnings = 0; #if Default_TOPN == Infinity char topn_specified = No; #endif char ch; char *iptr; char no_command = 1; struct timeval timeout; #ifdef ORDER char *order_name = NULL; int order_index = 0; #endif #ifndef FD_SET /* FD_SET and friends are not present: fake it */ typedef int fd_set; #define FD_ZERO(x) (*(x) = 0) #define FD_SET(f, x) (*(x) = 1< 0) { if ((myname = strrchr(argv[0], '/')) == 0) { myname = argv[0]; } else { myname++; } } /* initialize some selection options */ ps.idle = Yes; ps.self = -1; ps.system = No; ps.uid = -1; ps.thread = No; ps.wcpu = 1; ps.jid = -1; ps.jail = No; + ps.swap = No; ps.kidle = Yes; ps.command = NULL; /* get preset options from the environment */ if ((env_top = getenv("TOP")) != NULL) { av = preset_argv = argparse(env_top, &preset_argc); ac = preset_argc; /* set the dummy argument to an explanatory message, in case getopt encounters a bad argument */ preset_argv[0] = "while processing environment"; } /* process options */ do { /* if we're done doing the presets, then process the real arguments */ if (preset_argc == 0) { ac = argc; av = argv; /* this should keep getopt happy... */ optind = 1; } - while ((i = getopt(ac, av, "CSIHPabijJ:nquvzs:d:U:m:o:t")) != EOF) + while ((i = getopt(ac, av, "CSIHPabijJ:nquvzs:d:U:m:o:tw")) != EOF) { switch(i) { case 'v': /* show version number */ fprintf(stderr, "%s: version %s\n", myname, version_string()); exit(1); break; case 'u': /* toggle uid/username display */ do_unames = !do_unames; break; case 'U': /* display only username's processes */ if ((ps.uid = userid(optarg)) == -1) { fprintf(stderr, "%s: unknown user\n", optarg); exit(1); } break; case 'S': /* show system processes */ ps.system = !ps.system; break; case 'I': /* show idle processes */ ps.idle = !ps.idle; break; case 'i': /* go interactive regardless */ interactive = Yes; break; case 'n': /* batch, or non-interactive */ case 'b': interactive = No; break; case 'a': fmt_flags ^= FMT_SHOWARGS; break; case 'd': /* number of displays to show */ if ((i = atoiwi(optarg)) == Invalid || i == 0) { fprintf(stderr, "%s: warning: display count should be positive -- option ignored\n", myname); warnings++; } else { displays = i; } break; case 's': if ((delay = atoi(optarg)) < 0 || (delay == 0 && getuid() != 0)) { fprintf(stderr, "%s: warning: seconds delay should be positive -- using default\n", myname); delay = Default_DELAY; warnings++; } break; case 'q': /* be quick about it */ /* only allow this if user is really root */ if (getuid() == 0) { /* be very un-nice! */ (void) nice(-20); } else { fprintf(stderr, "%s: warning: `-q' option can only be used by root\n", myname); warnings++; } break; case 'm': /* select display mode */ if (strcmp(optarg, "io") == 0) { displaymode = DISP_IO; } else if (strcmp(optarg, "cpu") == 0) { displaymode = DISP_CPU; } else { fprintf(stderr, "%s: warning: `-m' option can only take args " "'io' or 'cpu'\n", myname); exit(1); } break; case 'o': /* select sort order */ #ifdef ORDER order_name = optarg; #else fprintf(stderr, "%s: this platform does not support arbitrary ordering. Sorry.\n", myname); warnings++; #endif break; case 't': ps.self = (ps.self == -1) ? getpid() : -1; break; case 'C': ps.wcpu = !ps.wcpu; break; case 'H': ps.thread = !ps.thread; break; case 'j': ps.jail = !ps.jail; break; case 'J': /* display only jail's processes */ if ((ps.jid = jail_getid(optarg)) == -1) { fprintf(stderr, "%s: unknown jail\n", optarg); exit(1); } ps.jail = 1; break; case 'P': pcpu_stats = !pcpu_stats; break; + case 'w': + ps.swap = 1; + break; + case 'z': ps.kidle = !ps.kidle; break; default: fprintf(stderr, "Top version %s\n" "Usage: %s [-abCHIijnPqStuvz] [-d count] [-m io | cpu] [-o field] [-s time]\n" " [-J jail] [-U username] [number]\n", version_string(), myname); exit(1); } } /* get count of top processes to display (if any) */ if (optind < ac) { if ((topn = atoiwi(av[optind])) == Invalid) { fprintf(stderr, "%s: warning: process display count should be non-negative -- using default\n", myname); warnings++; } #if Default_TOPN == Infinity else { topn_specified = Yes; } #endif } /* tricky: remember old value of preset_argc & set preset_argc = 0 */ i = preset_argc; preset_argc = 0; /* repeat only if we really did the preset arguments */ } while (i != 0); /* set constants for username/uid display correctly */ if (!do_unames) { uname_field = " UID "; get_userid = itoa7; } /* initialize the kernel memory interface */ if (machine_init(&statics, do_unames) == -1) { exit(1); } #ifdef ORDER /* determine sorting order index, if necessary */ if (order_name != NULL) { if ((order_index = string_index(order_name, statics.order_names)) == -1) { char **pp; fprintf(stderr, "%s: '%s' is not a recognized sorting order.\n", myname, order_name); fprintf(stderr, "\tTry one of these:"); pp = statics.order_names; while (*pp != NULL) { fprintf(stderr, " %s", *pp++); } fputc('\n', stderr); exit(1); } } #endif #ifdef no_initialization_needed /* initialize the hashing stuff */ if (do_unames) { init_hash(); } #endif /* initialize termcap */ init_termcap(interactive); /* get the string to use for the process area header */ header_text = format_header(uname_field); /* initialize display interface */ if ((max_topn = display_init(&statics)) == -1) { fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); exit(4); } /* print warning if user requested more processes than we can display */ if (topn > max_topn) { fprintf(stderr, "%s: warning: this terminal can only display %d processes.\n", myname, max_topn); warnings++; } /* adjust for topn == Infinity */ if (topn == Infinity) { /* * For smart terminals, infinity really means everything that can * be displayed, or Largest. * On dumb terminals, infinity means every process in the system! * We only really want to do that if it was explicitly specified. * This is always the case when "Default_TOPN != Infinity". But if * topn wasn't explicitly specified and we are on a dumb terminal * and the default is Infinity, then (and only then) we use * "Nominal_TOPN" instead. */ #if Default_TOPN == Infinity topn = smart_terminal ? Largest : (topn_specified ? Largest : Nominal_TOPN); #else topn = Largest; #endif } /* set header display accordingly */ display_header(topn > 0); /* determine interactive state */ if (interactive == Maybe) { interactive = smart_terminal; } /* if # of displays not specified, fill it in */ if (displays == 0) { displays = smart_terminal ? Infinity : 1; } /* hold interrupt signals while setting up the screen and the handlers */ #ifdef SIGHOLD sighold(SIGINT); sighold(SIGQUIT); sighold(SIGTSTP); #else old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP)); #endif init_screen(); (void) signal(SIGINT, leave); (void) signal(SIGQUIT, leave); (void) signal(SIGTSTP, tstop); #ifdef SIGWINCH (void) signal(SIGWINCH, winch); #endif #ifdef SIGRELSE sigrelse(SIGINT); sigrelse(SIGQUIT); sigrelse(SIGTSTP); #else (void) sigsetmask(old_sigmask); #endif if (warnings) { fputs("....", stderr); fflush(stderr); /* why must I do this? */ sleep((unsigned)(3 * warnings)); fputc('\n', stderr); } restart: /* * main loop -- repeat while display count is positive or while it * indicates infinity (by being -1) */ while ((displays == -1) || (displays-- > 0)) { int (*compare)(); /* get the current stats */ get_system_info(&system_info); #ifdef ORDER compare = compares[order_index]; #else if (displaymode == DISP_CPU) compare = proc_compare; else compare = io_compare; #endif /* get the current set of processes */ processes = get_process_info(&system_info, &ps, compare); /* display the load averages */ (*d_loadave)(system_info.last_pid, system_info.load_avg); /* display the current time */ /* this method of getting the time SHOULD be fairly portable */ time(&curr_time); i_uptime(&system_info.boottime, &curr_time); i_timeofday(&curr_time); /* display process state breakdown */ (*d_procstates)(system_info.p_total, system_info.procstates); /* display the cpu state percentage breakdown */ if (dostates) /* but not the first time */ { (*d_cpustates)(system_info.cpustates); } else { /* we'll do it next time */ if (smart_terminal) { z_cpustates(); } else { putchar('\n'); } dostates = Yes; } /* display memory stats */ (*d_memory)(system_info.memory); (*d_arc)(system_info.arc); /* display swap stats */ (*d_swap)(system_info.swap); /* handle message area */ (*d_message)(); /* update the header area */ (*d_header)(header_text); if (topn > 0) { /* determine number of processes to actually display */ /* this number will be the smallest of: active processes, number user requested, number current screen accomodates */ active_procs = system_info.P_ACTIVE; if (active_procs > topn) { active_procs = topn; } if (active_procs > max_topn) { active_procs = max_topn; } /* now show the top "n" processes. */ for (i = 0; i < active_procs; i++) { (*d_process)(i, format_next_process(processes, get_userid, fmt_flags)); } } else { i = 0; } /* do end-screen processing */ u_endscreen(i); /* now, flush the output buffer */ if (fflush(stdout) != 0) { new_message(MT_standout, " Write error on stdout"); putchar('\r'); quit(1); /*NOTREACHED*/ } /* only do the rest if we have more displays to show */ if (displays) { /* switch out for new display on smart terminals */ if (smart_terminal) { if (overstrike) { reset_display(); } else { d_loadave = u_loadave; d_procstates = u_procstates; d_cpustates = u_cpustates; d_memory = u_memory; d_arc = u_arc; d_swap = u_swap; d_message = u_message; d_header = u_header; d_process = u_process; } } no_command = Yes; if (!interactive) { sleep(delay); if (leaveflag) { end_screen(); exit(0); } } else while (no_command) { /* assume valid command unless told otherwise */ no_command = No; /* set up arguments for select with timeout */ FD_ZERO(&readfds); FD_SET(0, &readfds); /* for standard input */ timeout.tv_sec = delay; timeout.tv_usec = 0; if (leaveflag) { end_screen(); exit(0); } if (tstopflag) { /* move to the lower left */ end_screen(); fflush(stdout); /* default the signal handler action */ (void) signal(SIGTSTP, SIG_DFL); /* unblock the signal and send ourselves one */ #ifdef SIGRELSE sigrelse(SIGTSTP); #else (void) sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1))); #endif (void) kill(0, SIGTSTP); /* reset the signal handler */ (void) signal(SIGTSTP, tstop); /* reinit screen */ reinit_screen(); reset_display(); tstopflag = 0; goto restart; } if (winchflag) { /* reascertain the screen dimensions */ get_screensize(); /* tell display to resize */ max_topn = display_resize(); /* reset the signal handler */ (void) signal(SIGWINCH, winch); reset_display(); winchflag = 0; goto restart; } /* wait for either input or the end of the delay period */ sel_ret = select(2, &readfds, NULL, NULL, &timeout); if (sel_ret < 0 && errno != EINTR) quit(0); if (sel_ret > 0) { int newval; char *errmsg; /* something to read -- clear the message area first */ clear_message(); /* now read it and convert to command strchr */ /* (use "change" as a temporary to hold strchr) */ if (read(0, &ch, 1) != 1) { /* read error: either 0 or -1 */ new_message(MT_standout, " Read error on stdin"); putchar('\r'); quit(1); /*NOTREACHED*/ } if ((iptr = strchr(command_chars, ch)) == NULL) { if (ch != '\r' && ch != '\n') { /* illegal command */ new_message(MT_standout, " Command not understood"); } putchar('\r'); no_command = Yes; } else { change = iptr - command_chars; if (overstrike && change > CMD_OSLIMIT) { /* error */ new_message(MT_standout, " Command cannot be handled by this terminal"); putchar('\r'); no_command = Yes; } else switch(change) { case CMD_redraw: /* redraw screen */ reset_display(); break; case CMD_update: /* merely update display */ /* is the load average high? */ if (system_info.load_avg[0] > LoadMax) { /* yes, go home for visual feedback */ go_home(); fflush(stdout); } break; case CMD_quit: /* quit */ quit(0); /*NOTREACHED*/ break; case CMD_help1: /* help */ case CMD_help2: reset_display(); clear(); show_help(); standout("Hit any key to continue: "); fflush(stdout); (void) read(0, &ch, 1); break; case CMD_errors: /* show errors */ if (error_count() == 0) { new_message(MT_standout, " Currently no errors to report."); putchar('\r'); no_command = Yes; } else { reset_display(); clear(); show_errors(); standout("Hit any key to continue: "); fflush(stdout); (void) read(0, &ch, 1); } break; case CMD_number1: /* new number */ case CMD_number2: new_message(MT_standout, "Number of processes to show: "); newval = readline(tempbuf1, 8, Yes); if (newval > -1) { if (newval > max_topn) { new_message(MT_standout | MT_delayed, " This terminal can only display %d processes.", max_topn); putchar('\r'); } if (newval == 0) { /* inhibit the header */ display_header(No); } else if (newval > topn && topn == 0) { /* redraw the header */ display_header(Yes); d_header = i_header; } topn = newval; } break; case CMD_delay: /* new seconds delay */ new_message(MT_standout, "Seconds to delay: "); if ((i = readline(tempbuf1, 8, Yes)) > -1) { if ((delay = i) == 0 && getuid() != 0) { delay = 1; } } clear_message(); break; case CMD_displays: /* change display count */ new_message(MT_standout, "Displays to show (currently %s): ", displays == -1 ? "infinite" : itoa(displays)); if ((i = readline(tempbuf1, 10, Yes)) > 0) { displays = i; } else if (i == 0) { quit(0); } clear_message(); break; case CMD_kill: /* kill program */ new_message(0, "kill "); if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) { if ((errmsg = kill_procs(tempbuf2)) != NULL) { new_message(MT_standout, "%s", errmsg); putchar('\r'); no_command = Yes; } } else { clear_message(); } break; case CMD_renice: /* renice program */ new_message(0, "renice "); if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) { if ((errmsg = renice_procs(tempbuf2)) != NULL) { new_message(MT_standout, "%s", errmsg); putchar('\r'); no_command = Yes; } } else { clear_message(); } break; case CMD_idletog: case CMD_idletog2: ps.idle = !ps.idle; new_message(MT_standout | MT_delayed, " %sisplaying idle processes.", ps.idle ? "D" : "Not d"); putchar('\r'); break; case CMD_selftog: ps.self = (ps.self == -1) ? getpid() : -1; new_message(MT_standout | MT_delayed, " %sisplaying self.", (ps.self == -1) ? "D" : "Not d"); putchar('\r'); break; case CMD_user: new_message(MT_standout, "Username to show (+ for all): "); if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) { if (tempbuf2[0] == '+' && tempbuf2[1] == '\0') { ps.uid = -1; } else if ((i = userid(tempbuf2)) == -1) { new_message(MT_standout, " %s: unknown user", tempbuf2); no_command = Yes; } else { ps.uid = i; } putchar('\r'); } else { clear_message(); } break; case CMD_thrtog: ps.thread = !ps.thread; new_message(MT_standout | MT_delayed, " Displaying threads %s", ps.thread ? "separately" : "as a count"); header_text = format_header(uname_field); reset_display(); putchar('\r'); break; case CMD_wcputog: ps.wcpu = !ps.wcpu; new_message(MT_standout | MT_delayed, " Displaying %s CPU", ps.wcpu ? "weighted" : "raw"); header_text = format_header(uname_field); reset_display(); putchar('\r'); break; case CMD_viewtog: if (++displaymode == DISP_MAX) displaymode = 0; header_text = format_header(uname_field); display_header(Yes); d_header = i_header; reset_display(); break; case CMD_viewsys: ps.system = !ps.system; break; case CMD_showargs: fmt_flags ^= FMT_SHOWARGS; break; #ifdef ORDER case CMD_order: new_message(MT_standout, "Order to sort: "); if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) { if ((i = string_index(tempbuf2, statics.order_names)) == -1) { new_message(MT_standout, " %s: unrecognized sorting order", tempbuf2); no_command = Yes; } else { order_index = i; } putchar('\r'); } else { clear_message(); } break; #endif case CMD_jidtog: ps.jail = !ps.jail; new_message(MT_standout | MT_delayed, " %sisplaying jail ID.", ps.jail ? "D" : "Not d"); header_text = format_header(uname_field); reset_display(); putchar('\r'); break; case CMD_jail: new_message(MT_standout, "Jail to show (+ for all): "); if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) { if (tempbuf2[0] == '+' && tempbuf2[1] == '\0') { ps.jid = -1; } else if ((i = jail_getid(tempbuf2)) == -1) { new_message(MT_standout, " %s: unknown jail", tempbuf2); no_command = Yes; } else { ps.jid = i; } if (ps.jail == 0) { ps.jail = 1; new_message(MT_standout | MT_delayed, " Displaying jail " "ID."); header_text = format_header(uname_field); reset_display(); } putchar('\r'); } else { clear_message(); } break; case CMD_kidletog: ps.kidle = !ps.kidle; new_message(MT_standout | MT_delayed, " %sisplaying system idle process.", ps.kidle ? "D" : "Not d"); putchar('\r'); break; case CMD_pcputog: pcpu_stats = !pcpu_stats; new_message(MT_standout | MT_delayed, " Displaying %sCPU statistics.", pcpu_stats ? "per-" : "global "); toggle_pcpustats(); max_topn = display_updatecpus(&statics); + reset_display(); + putchar('\r'); + break; + case CMD_swaptog: + ps.swap = !ps.swap; + new_message(MT_standout | MT_delayed, + " %sisplaying per-process swap usage.", + ps.swap ? "D" : "Not d"); + header_text = format_header(uname_field); reset_display(); putchar('\r'); break; default: new_message(MT_standout, " BAD CASE IN SWITCH!"); putchar('\r'); } } /* flush out stuff that may have been written */ fflush(stdout); } } } } #ifdef DEBUG fclose(debug); #endif quit(0); /*NOTREACHED*/ } /* * reset_display() - reset all the display routine pointers so that entire * screen will get redrawn. */ void reset_display() { d_loadave = i_loadave; d_procstates = i_procstates; d_cpustates = i_cpustates; d_memory = i_memory; d_arc = i_arc; d_swap = i_swap; d_message = i_message; d_header = i_header; d_process = i_process; } /* * signal handlers */ sigret_t leave() /* exit under normal conditions -- INT handler */ { leaveflag = 1; } sigret_t tstop(i) /* SIGTSTP handler */ int i; { tstopflag = 1; } #ifdef SIGWINCH sigret_t winch(i) /* SIGWINCH handler */ int i; { winchflag = 1; } #endif void quit(status) /* exit under duress */ int status; { end_screen(); exit(status); /*NOTREACHED*/ } Index: projects/clang390-import/contrib/top/top.xs =================================================================== --- projects/clang390-import/contrib/top/top.xs (revision 305430) +++ projects/clang390-import/contrib/top/top.xs (revision 305431) @@ -1,448 +1,457 @@ .\" NOTE: changes to the manual page for "top" should be made in the .\" file "top.X" and NOT in the file "top.1". .\" $FreeBSD$ .nr N %topn% .nr D %delay% .TH TOP 1 Local .UC 4 .SH NAME top \- display and update information about the top cpu processes .SH SYNOPSIS .B top [ -.B \-abCHIijnPqStuvz +.B \-abCHIijnPqStuvwz ] [ .BI \-d count ] [ .BI \-m io | cpu ] [ .BI \-o field ] [ .BI \-s time ] [ .BI \-J jail ] [ .BI \-U username ] [ .I number ] .SH DESCRIPTION .\" This defines appropriate quote strings for nroff and troff .ds lq \&" .ds rq \&" .if t .ds lq `` .if t .ds rq '' .\" Just in case these number registers aren't set yet... .if \nN==0 .nr N 10 .if \nD==0 .nr D 2 .I Top displays the top .if !\nN==-1 \nN processes on the system and periodically updates this information. .if \nN==-1 \ \{\ If standard output is an intelligent terminal (see below) then as many processes as will fit on the terminal screen are displayed by default. Otherwise, a good number of them are shown (around 20). .\} Raw cpu percentage is used to rank the processes. If .I number is given, then the top .I number processes will be displayed instead of the default. .PP .I Top makes a distinction between terminals that support advanced capabilities and those that do not. This distinction affects the choice of defaults for certain options. In the remainder of this document, an \*(lqintelligent\*(rq terminal is one that supports cursor addressing, clear screen, and clear to end of line. Conversely, a \*(lqdumb\*(rq terminal is one that does not support such features. If the output of .I top is redirected to a file, it acts as if it were being run on a dumb terminal. .SH OPTIONS .TP .B \-C Toggle CPU display mode. By default top displays the weighted CPU percentage in the WCPU column (this is the same value that .IR ps (1) displays as CPU). Each time .B \-C flag is passed it toggles between \*(lqraw cpu\*(rq mode and \*(lqweighted cpu\*(rq mode, showing the \*(lqCPU\*(rq or the \*(lqWCPU\*(rq column respectively. .TP .B \-S Show system processes in the display. Normally, system processes such as the pager and the swapper are not shown. This option makes them visible. .TP .B \-a Display command names derived from the argv[] vector, rather than real executable name. It's useful when you want to watch applications, that puts their status information there. If the real name differs from argv[0], it will be displayed in parenthesis. .TP .B \-b Use \*(lqbatch\*(rq mode. In this mode, all input from the terminal is ignored. Interrupt characters (such as ^C and ^\e) still have an effect. This is the default on a dumb terminal, or when the output is not a terminal. .TP .B \-H Display each thread for a multithreaded process individually. By default a single summary line is displayed for each process. .TP .B \-i Use \*(lqinteractive\*(rq mode. In this mode, any input is immediately read for processing. See the section on \*(lqInteractive Mode\*(rq for an explanation of which keys perform what functions. After the command is processed, the screen will immediately be updated, even if the command was not understood. This mode is the default when standard output is an intelligent terminal. .TP .B \-I Do not display idle processes. By default, top displays both active and idle processes. .TP .B \-j Display the .IR jail (8) ID. .TP .B \-t Do not display the .I top process. .TP .BI \-m display Display either 'cpu' or 'io' statistics. Default is 'cpu'. .TP .B \-n Use \*(lqnon-interactive\*(rq mode. This is identical to \*(lqbatch\*(rq mode. .TP .B \-P Display per-cpu CPU usage statistics. .TP .B \-q Renice .I top to -20 so that it will run faster. This can be used when the system is being very sluggish to improve the possibility of discovering the problem. This option can only be used by root. .TP .B \-u Do not take the time to map uid numbers to usernames. Normally, .I top will read as much of the file \*(lq/etc/passwd\*(rq as is necessary to map all the user id numbers it encounters into login names. This option disables all that, while possibly decreasing execution time. The uid numbers are displayed instead of the names. .TP .B \-v Write version number information to stderr then exit immediately. No other processing takes place when this option is used. To see current revision information while top is running, use the help command \*(lq?\*(rq. .TP +.B \-w +Display approximate swap usage for each process. +.TP .B \-z Do not display the system idle process. .TP .BI \-d count Show only .I count displays, then exit. A display is considered to be one update of the screen. This option allows the user to select the number of displays he wants to see before .I top automatically exits. For intelligent terminals, no upper limit is set. The default is 1 for dumb terminals. .TP .BI \-s time Set the delay between screen updates to .I time seconds. The default delay between updates is \nD seconds. .TP .BI \-o field -Sort the process display area on the specified field. The field name is -the name of the column as seen in the output, but in lower case. Likely -values are \*(lqcpu\*(rq, \*(lqsize\*(rq, \*(lqres\*(rq, and \*(lqtime\*(rq, -but may vary on different operating systems. Note that -not all operating systems support this option. +Sort the process display area on the specified field. The field name +is the name of the column as seen in the output, but in lower case: +\*(lqcpu\*(lq, \*(rqsize\*(lq, \*(rqres\*(lq, \*(rqtime\*(lq, +\*(rqpri\*(lq, \*(rqthreads\*(lq, \*(lqtotal\*(lq, \*(rqread\*(lq, +\*(rqwrite\*(lq, \*(rqfault\*(lq, \*(rqvcsw\*(lq, \*(rqivcsw\*(lq, +\*(lqjid\*(lq, \*(rqswap\*(lq or \*(rqpid\*(lq. .TP .BI \-J jail Show only those processes owned by .IR jail . This may be either the .B jid or .B name of the jail. Use .B 0 to limit to host processes. Using this option implies the .B \-j flag. .PP .BI \-U username Show only those processes owned by .IR username . This option currently only accepts usernames and will not understand uid numbers. .PP Both .I count and .I number fields can be specified as \*(lqinfinite\*(rq, indicating that they can stretch as far as possible. This is accomplished by using any proper prefix of the keywords \*(lqinfinity\*(rq, \*(lqmaximum\*(rq, or \*(lqall\*(rq. The default for .I count on an intelligent terminal is, in fact, .BI infinity . .PP The environment variable .B TOP is examined for options before the command line is scanned. This enables a user to set his or her own defaults. The number of processes to display can also be specified in the environment variable .BR TOP . The options .BR \-a , .BR \-C , .BR \-H , .BR \-I , .BR \-j , .BR \-P , .BR \-S , .BR \-t , .BR \-u , +.BR \-w , and .B \-z are actually toggles. A second specification of any of these options will negate the first. Thus a user who has the environment variable .B TOP set to \*(lq\-I\*(rq may use the command \*(lqtop \-I\*(rq to see idle processes. .SH "INTERACTIVE MODE" When .I top is running in \*(lqinteractive mode\*(rq, it reads commands from the terminal and acts upon them accordingly. In this mode, the terminal is put in \*(lqCBREAK\*(rq, so that a character will be processed as soon as it is typed. Almost always, a key will be pressed when .I top is between displays; that is, while it is waiting for .I time seconds to elapse. If this is the case, the command will be processed and the display will be updated immediately thereafter (reflecting any changes that the command may have specified). This happens even if the command was incorrect. If a key is pressed while .I top is in the middle of updating the display, it will finish the update and then process the command. Some commands require additional information, and the user will be prompted accordingly. While typing this information in, the user's erase and kill keys (as set up by the command .IR stty ) are recognized, and a newline terminates the input. .PP These commands are currently recognized (^L refers to control-L): .TP .B ^L Redraw the screen. .IP "\fBh\fP\ or\ \fB?\fP" Display a summary of the commands (help screen). Version information is included in this display. .TP .B q Quit .IR top. .TP .B d Change the number of displays to show (prompt for new number). Remember that the next display counts as one, so typing .B d1 will make .I top show one final display and then immediately exit. .TP .B m Toggle the display between 'cpu' and 'io' modes. .TP .B n or # Change the number of processes to display (prompt for new number). .TP .B s Change the number of seconds to delay between displays (prompt for new number). .TP .B S Toggle the display of system processes. .TP .B a Toggle the display of process titles. .TP .B k Send a signal (\*(lqkill\*(rq by default) to a list of processes. This acts similarly to the command .IR kill (1)). .TP .B r Change the priority (the \*(lqnice\*(rq) of a list of processes. This acts similarly to the command .IR renice (8)). .TP .B u Display only processes owned by a specific username (prompt for username). If the username specified is simply \*(lq+\*(rq, then processes belonging to all users will be displayed. .TP .B o Change the order in which the display is sorted. This command is not available on all systems. The sort key names vary from system to system but usually include: \*(lqcpu\*(rq, \*(lqres\*(rq, \*(lqsize\*(rq, \*(lqtime\*(rq. The default is cpu. .TP .B e Display a list of system errors (if any) generated by the last .BR k ill or .BR r enice command. .TP .B H Toggle the display of threads. .TP .B i (or .BR I ) Toggle the display of idle processes. .TP .B j Toggle the display of .IR jail (8) ID. .TP .B J Display only processes owned by a specific jail (prompt for jail). If the jail specified is simply \*(lq+\*(rq, then processes belonging to all jails and the host will be displayed. This will also enable the display of JID. .TP .B P Toggle the display of per-CPU statistics. .TP .B t Toggle the display of the .I top process. .TP +.B w +Toggle the display of swap usage. +.TP .B z Toggle the display of the system idle process. .SH "THE DISPLAY" The actual display varies depending on the specific variant of Unix that the machine is running. This description may not exactly match what is seen by top running on this particular machine. Differences are listed at the end of this manual entry. .PP The top few lines of the display show general information about the state of the system, including the last process id assigned to a process (on most systems), the three load averages, the current time, the number of existing processes, the number of processes in each state (sleeping, running, starting, zombies, and stopped), and a percentage of time spent in each of the processor states (user, nice, system, and idle). It also includes information about physical and virtual memory allocation. .PP The remainder of the screen displays information about individual processes. This display is similar in spirit to .IR ps (1) but it is not exactly the same. PID is the process id, JID, when displayed, is the .IR jail (8) ID corresponding to the process, USERNAME is the name of the process's owner (if .B \-u is specified, a UID column will be substituted for USERNAME), PRI is the current priority of the process, NICE is the nice amount (in the range \-20 to 20), SIZE is the total size of the process (text, data, and stack), -RES is the current amount of resident memory (both SIZE and RES are -given in kilobytes), +RES is the current amount of resident memory, +SWAP is the approximate amount of swap, if enabled +(SIZE, RES and SWAP are given in kilobytes), STATE is the current state (one of \*(lqSTART\*(rq, \*(lqRUN\*(rq (shown as \*(lqCPUn\*(rq on SMP systems), \*(lqSLEEP\*(rq, \*(lqSTOP\*(rq, \*(lqZOMB\*(rq, \*(lqWAIT\*(rq, \*(lqLOCK\*(rq or the event on which the process waits), C is the processor number on which the process is executing (visible only on SMP systems), TIME is the number of system and user cpu seconds that the process has used, WCPU, when displayed, is the weighted cpu percentage (this is the same value that .IR ps (1) displays as CPU), CPU is the raw percentage and is the field that is sorted to determine the order of the processes, and COMMAND is the name of the command that the process is currently running (if the process is swapped out, this column is marked \*(lq\*(rq). .SH NOTES If a process is in the \*(lqSLEEP\*(rq or \*(lqLOCK\*(rq state, the state column will report the name of the event or lock on which the process is waiting. Lock names are prefixed with an asterisk \*(lq*\*(rq while sleep events are not. .SH AUTHOR William LeFebvre, EECS Department, Northwestern University .SH ENVIRONMENT .DT TOP user-configurable defaults for options. .SH FILES .DT /dev/kmem kernel memory .br /dev/mem physical memory .br /etc/passwd used to map uid numbers to user names .br /boot/kernel/kernel system image .SH BUGS Don't shoot me, but the default for .B \-I has changed once again. So many people were confused by the fact that .I top wasn't showing them all the processes that I have decided to make the default behavior show idle processes, just like it did in version 2. But to appease folks who can't stand that behavior, I have added the ability to set \*(lqdefault\*(rq options in the environment variable .B TOP (see the OPTIONS section). Those who want the behavior that version 3.0 had need only set the environment variable .B TOP to \*(lq\-I\*(rq. .PP The command name for swapped processes should be tracked down, but this would make the program run slower. .PP As with .IR ps (1), things can change while .I top is collecting information for an update. The picture it gives is only a close approximation to reality. .SH "SEE ALSO" kill(1), ps(1), stty(1), mem(4), renice(8) Index: projects/clang390-import/contrib/top =================================================================== --- projects/clang390-import/contrib/top (revision 305430) +++ projects/clang390-import/contrib/top (revision 305431) Property changes on: projects/clang390-import/contrib/top ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/contrib/top:r303250-305430 Index: projects/clang390-import/lib/libarchive/config_freebsd.h =================================================================== --- projects/clang390-import/lib/libarchive/config_freebsd.h (revision 305430) +++ projects/clang390-import/lib/libarchive/config_freebsd.h (revision 305431) @@ -1,260 +1,262 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include /* FreeBSD 5.0 and later have ACL and extattr support. */ #if __FreeBSD__ > 4 #define HAVE_ACL_CREATE_ENTRY 1 +#define HAVE_ACL_GET_FD_NP 1 #define HAVE_ACL_GET_LINK_NP 1 #define HAVE_ACL_GET_PERM_NP 1 #define HAVE_ACL_INIT 1 #define HAVE_ACL_IS_TRIVIAL_NP 1 #define HAVE_ACL_PERMSET_T 1 #define HAVE_ACL_SET_FD 1 #define HAVE_ACL_SET_FD_NP 1 #define HAVE_ACL_SET_FILE 1 #define HAVE_ACL_SET_LINK_NP 1 #define HAVE_ACL_USER 1 #define HAVE_ARC4RANDOM_BUF 1 #define HAVE_EXTATTR_GET_FILE 1 #define HAVE_EXTATTR_LIST_FILE 1 #define HAVE_EXTATTR_SET_FD 1 #define HAVE_EXTATTR_SET_FILE 1 +#define HAVE_STRUCT_XVFSCONF 1 #define HAVE_SYS_ACL_H 1 #define HAVE_SYS_EXTATTR_H 1 #endif #ifdef WITH_OPENSSL #define HAVE_LIBCRYPTO 1 #define HAVE_OPENSSL_EVP_H 1 #define HAVE_OPENSSL_MD5_H 1 #define HAVE_OPENSSL_RIPEMD_H 1 #define HAVE_OPENSSL_SHA_H 1 #define HAVE_OPENSSL_SHA256_INIT 1 #define HAVE_OPENSSL_SHA384_INIT 1 #define HAVE_OPENSSL_SHA512_INIT 1 #define HAVE_PKCS5_PBKDF2_HMAC_SHA1 1 #define HAVE_SHA256 1 #define HAVE_SHA384 1 #define HAVE_SHA512 1 #else #define HAVE_LIBMD 1 #define HAVE_MD5_H 1 #define HAVE_MD5INIT 1 #define HAVE_RIPEMD_H 1 #define HAVE_SHA_H 1 #define HAVE_SHA1 1 #define HAVE_SHA1_INIT 1 #define HAVE_SHA256 1 #define HAVE_SHA256_H 1 #define HAVE_SHA256_INIT 1 #define HAVE_SHA512 1 #define HAVE_SHA512_H 1 #define HAVE_SHA512_INIT 1 #endif #define HAVE_BSDXML_H 1 #define HAVE_BZLIB_H 1 #define HAVE_CHFLAGS 1 #define HAVE_CHOWN 1 #define HAVE_CHROOT 1 #define HAVE_CTIME_R 1 #define HAVE_CTYPE_H 1 #define HAVE_DECL_EXTATTR_NAMESPACE_USER 1 #define HAVE_DECL_INT32_MAX 1 #define HAVE_DECL_INT32_MIN 1 #define HAVE_DECL_INT64_MAX 1 #define HAVE_DECL_INT64_MIN 1 #define HAVE_DECL_INTMAX_MAX 1 #define HAVE_DECL_INTMAX_MIN 1 #define HAVE_DECL_SIZE_MAX 1 #define HAVE_DECL_SSIZE_MAX 1 #define HAVE_DECL_STRERROR_R 1 #define HAVE_DECL_UINT32_MAX 1 #define HAVE_DECL_UINT64_MAX 1 #define HAVE_DECL_UINTMAX_MAX 1 #define HAVE_DIRENT_H 1 #define HAVE_DLFCN_H 1 #define HAVE_D_MD_ORDER 1 #define HAVE_EFTYPE 1 #define HAVE_EILSEQ 1 #define HAVE_ERRNO_H 1 #define HAVE_FCHDIR 1 #define HAVE_FCHFLAGS 1 #define HAVE_FCHMOD 1 #define HAVE_FCHOWN 1 #define HAVE_FCNTL 1 #define HAVE_FCNTL_H 1 #define HAVE_FDOPENDIR 1 #define HAVE_FORK 1 #define HAVE_FSEEKO 1 #define HAVE_FSTAT 1 #define HAVE_FSTATAT 1 #define HAVE_FSTATFS 1 #define HAVE_FSTATVFS 1 #define HAVE_FTRUNCATE 1 #define HAVE_FUTIMES 1 #define HAVE_FUTIMESAT 1 #define HAVE_GETEUID 1 #define HAVE_GETGRGID_R 1 #define HAVE_GETGRNAM_R 1 #define HAVE_GETPID 1 #define HAVE_GETPWNAM_R 1 #define HAVE_GETPWUID_R 1 #define HAVE_GETVFSBYNAME 1 #define HAVE_GMTIME_R 1 #define HAVE_GRP_H 1 #define HAVE_INTMAX_T 1 #define HAVE_INTTYPES_H 1 #define HAVE_LANGINFO_H 1 #define HAVE_LCHFLAGS 1 #define HAVE_LCHMOD 1 #define HAVE_LCHOWN 1 #define HAVE_LIBZ 1 #define HAVE_LIMITS_H 1 #define HAVE_LINK 1 #define HAVE_LOCALE_H 1 #define HAVE_LOCALTIME_R 1 #define HAVE_LONG_LONG_INT 1 #define HAVE_LSTAT 1 #define HAVE_LUTIMES 1 #define HAVE_MBRTOWC 1 #define HAVE_MEMMOVE 1 #define HAVE_MEMORY_H 1 #define HAVE_MEMSET 1 #define HAVE_MKDIR 1 #define HAVE_MKFIFO 1 #define HAVE_MKNOD 1 #define HAVE_MKSTEMP 1 #define HAVE_NL_LANGINFO 1 #define HAVE_OPENAT 1 #define HAVE_PATHS_H 1 #define HAVE_PIPE 1 #define HAVE_POLL 1 #define HAVE_POLL_H 1 #define HAVE_POSIX_SPAWNP 1 #define HAVE_PTHREAD_H 1 #define HAVE_PWD_H 1 #define HAVE_READDIR_R 1 #define HAVE_READLINK 1 #define HAVE_READLINKAT 1 #define HAVE_READPASSPHRASE 1 #define HAVE_READPASSPHRASE_H 1 #define HAVE_REGEX_H 1 #define HAVE_SELECT 1 #define HAVE_SETENV 1 #define HAVE_SETLOCALE 1 #define HAVE_SIGACTION 1 #define HAVE_SIGNAL_H 1 #define HAVE_SPAWN_H 1 #define HAVE_STATFS 1 #define HAVE_STATVFS 1 #define HAVE_STDARG_H 1 #define HAVE_STDINT_H 1 #define HAVE_STDLIB_H 1 #define HAVE_STRCHR 1 #define HAVE_STRDUP 1 #define HAVE_STRERROR 1 #define HAVE_STRERROR_R 1 #define HAVE_STRFTIME 1 #define HAVE_STRINGS_H 1 #define HAVE_STRING_H 1 #define HAVE_STRRCHR 1 #define HAVE_STRUCT_STATFS_F_NAMEMAX 1 #define HAVE_STRUCT_STAT_ST_BIRTHTIME 1 #define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1 #define HAVE_STRUCT_STAT_ST_BLKSIZE 1 #define HAVE_STRUCT_STAT_ST_FLAGS 1 #define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1 #define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1 #define HAVE_STRUCT_TM_TM_GMTOFF 1 #define HAVE_SYMLINK 1 #define HAVE_SYS_CDEFS_H 1 #define HAVE_SYS_IOCTL_H 1 #define HAVE_SYS_MOUNT_H 1 #define HAVE_SYS_PARAM_H 1 #define HAVE_SYS_POLL_H 1 #define HAVE_SYS_SELECT_H 1 #define HAVE_SYS_STATVFS_H 1 #define HAVE_SYS_STAT_H 1 #define HAVE_SYS_TIME_H 1 #define HAVE_SYS_TYPES_H 1 #define HAVE_SYS_UTSNAME_H 1 #define HAVE_SYS_WAIT_H 1 #define HAVE_TIMEGM 1 #define HAVE_TIME_H 1 #define HAVE_TZSET 1 #define HAVE_UINTMAX_T 1 #define HAVE_UNISTD_H 1 #define HAVE_UNSETENV 1 #define HAVE_UNSIGNED_LONG_LONG 1 #define HAVE_UNSIGNED_LONG_LONG_INT 1 #define HAVE_UTIME 1 #define HAVE_UTIMES 1 #define HAVE_UTIME_H 1 #define HAVE_VFORK 1 #define HAVE_VPRINTF 1 #define HAVE_WCHAR_H 1 #define HAVE_WCHAR_T 1 #define HAVE_WCRTOMB 1 #define HAVE_WCSCMP 1 #define HAVE_WCSCPY 1 #define HAVE_WCSLEN 1 #define HAVE_WCTOMB 1 #define HAVE_WCTYPE_H 1 #define HAVE_WMEMCMP 1 #define HAVE_WMEMCPY 1 #define HAVE_WMEMMOVE 1 #define HAVE_ZLIB_H 1 #define TIME_WITH_SYS_TIME 1 #if __FreeBSD_version >= 1100056 #define HAVE_FUTIMENS 1 #define HAVE_UTIMENSAT 1 #endif /* FreeBSD 4 and earlier lack intmax_t/uintmax_t */ #if __FreeBSD__ < 5 #define intmax_t int64_t #define uintmax_t uint64_t #endif /* FreeBSD defines for archive_hash.h */ #ifdef WITH_OPENSSL #define ARCHIVE_CRYPTO_MD5_OPENSSL 1 #define ARCHIVE_CRYPTO_RMD160_OPENSSL 1 #define ARCHIVE_CRYPTO_SHA1_OPENSSL #define ARCHIVE_CRYPTO_SHA256_OPENSSL 1 #define ARCHIVE_CRYPTO_SHA384_OPENSSL 1 #define ARCHIVE_CRYPTO_SHA512_OPENSSL 1 #else #define ARCHIVE_CRYPTO_MD5_LIBMD 1 #define ARCHIVE_CRYPTO_SHA1_LIBMD 1 #define ARCHIVE_CRYPTO_SHA256_LIBMD 1 #define ARCHIVE_CRYPTO_SHA512_LIBMD 1 #endif Index: projects/clang390-import/lib/libc/stdio/fgets.c =================================================================== --- projects/clang390-import/lib/libc/stdio/fgets.c (revision 305430) +++ projects/clang390-import/lib/libc/stdio/fgets.c (revision 305431) @@ -1,109 +1,115 @@ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)fgets.c 8.2 (Berkeley) 12/22/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" +#include #include #include #include "un-namespace.h" #include "local.h" #include "libc_private.h" /* * Read at most n-1 characters from the given file. * Stop when a newline has been read, or the count runs out. * Return first argument, or NULL if no characters were read. */ char * fgets(char * __restrict buf, int n, FILE * __restrict fp) { size_t len; char *s; unsigned char *p, *t; - if (n <= 0) /* sanity check */ - return (NULL); - FLOCKFILE(fp); ORIENT(fp, -1); + + if (n <= 0) { /* sanity check */ + fp->_flags |= __SERR; + errno = EINVAL; + FUNLOCKFILE(fp); + return (NULL); + } + s = buf; n--; /* leave space for NUL */ while (n != 0) { /* * If the buffer is empty, refill it. */ if ((len = fp->_r) <= 0) { if (__srefill(fp)) { /* EOF/error: stop with partial or no line */ - if (s == buf) { + if (!__sfeof(fp) || s == buf) { FUNLOCKFILE(fp); return (NULL); } break; } len = fp->_r; } p = fp->_p; /* * Scan through at most n bytes of the current buffer, * looking for '\n'. If found, copy up to and including * newline, and stop. Otherwise, copy entire chunk * and loop. */ if (len > n) len = n; t = memchr((void *)p, '\n', len); if (t != NULL) { len = ++t - p; fp->_r -= len; fp->_p = t; (void)memcpy((void *)s, (void *)p, len); s[len] = 0; FUNLOCKFILE(fp); return (buf); } fp->_r -= len; fp->_p += len; (void)memcpy((void *)s, (void *)p, len); s += len; n -= len; } *s = 0; FUNLOCKFILE(fp); return (buf); } Index: projects/clang390-import/lib/libc/stdio/fgetws.c =================================================================== --- projects/clang390-import/lib/libc/stdio/fgetws.c (revision 305430) +++ projects/clang390-import/lib/libc/stdio/fgetws.c (revision 305431) @@ -1,112 +1,128 @@ /*- * Copyright (c) 2002-2004 Tim J. Robbins. * All rights reserved. * * Copyright (c) 2011 The FreeBSD Foundation * All rights reserved. * Portions of this software were developed by David Chisnall * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include "un-namespace.h" #include "libc_private.h" #include "local.h" #include "mblocal.h" wchar_t * fgetws_l(wchar_t * __restrict ws, int n, FILE * __restrict fp, locale_t locale) { + int sret; wchar_t *wsp; size_t nconv; const char *src; unsigned char *nl; FIX_LOCALE(locale); struct xlocale_ctype *l = XLOCALE_CTYPE(locale); FLOCKFILE(fp); ORIENT(fp, 1); if (n <= 0) { + fp->_flags |= __SERR; errno = EINVAL; goto error; } + wsp = ws; + if (n == 1) + goto ok; + if (fp->_r <= 0 && __srefill(fp)) - /* EOF */ + /* EOF or ferror */ goto error; - wsp = ws; + + sret = 0; do { src = fp->_p; nl = memchr(fp->_p, '\n', fp->_r); nconv = l->__mbsnrtowcs(wsp, &src, nl != NULL ? (nl - fp->_p + 1) : fp->_r, n - 1, &fp->_mbstate); - if (nconv == (size_t)-1) + if (nconv == (size_t)-1) { /* Conversion error */ + fp->_flags |= __SERR; goto error; + } if (src == NULL) { /* * We hit a null byte. Increment the character count, * since mbsnrtowcs()'s return value doesn't include * the terminating null, then resume conversion * after the null. */ nconv++; src = memchr(fp->_p, '\0', fp->_r); src++; } fp->_r -= (unsigned char *)src - fp->_p; fp->_p = (unsigned char *)src; n -= nconv; wsp += nconv; - } while (wsp[-1] != L'\n' && n > 1 && (fp->_r > 0 || - __srefill(fp) == 0)); - if (wsp == ws) - /* EOF */ + } while ((wsp == ws || wsp[-1] != L'\n') && n > 1 && (fp->_r > 0 || + (sret = __srefill(fp)) == 0)); + if (sret && !__sfeof(fp)) + /* ferror */ goto error; - if (!l->__mbsinit(&fp->_mbstate)) + if (!l->__mbsinit(&fp->_mbstate)) { /* Incomplete character */ + fp->_flags |= __SERR; + errno = EILSEQ; goto error; + } + if (wsp == ws) + /* EOF */ + goto error; +ok: *wsp = L'\0'; FUNLOCKFILE(fp); - return (ws); error: FUNLOCKFILE(fp); return (NULL); } + wchar_t * fgetws(wchar_t * __restrict ws, int n, FILE * __restrict fp) { return fgetws_l(ws, n, fp, __get_locale()); } Index: projects/clang390-import/sys/arm/allwinner/aw_thermal.c =================================================================== --- projects/clang390-import/sys/arm/allwinner/aw_thermal.c (revision 305430) +++ projects/clang390-import/sys/arm/allwinner/aw_thermal.c (revision 305431) @@ -1,384 +1,420 @@ /*- * Copyright (c) 2016 Jared McNeill * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Allwinner thermal sensor controller */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define THS_CTRL0 0x00 #define THS_CTRL1 0x04 #define ADC_CALI_EN (1 << 17) #define THS_CTRL2 0x40 #define SENSOR_ACQ1_SHIFT 16 #define SENSOR2_EN (1 << 2) #define SENSOR1_EN (1 << 1) #define SENSOR0_EN (1 << 0) #define THS_INTC 0x44 #define THS_INTS 0x48 #define THS2_DATA_IRQ_STS (1 << 10) #define THS1_DATA_IRQ_STS (1 << 9) #define THS0_DATA_IRQ_STS (1 << 8) #define SHUT_INT2_STS (1 << 6) #define SHUT_INT1_STS (1 << 5) #define SHUT_INT0_STS (1 << 4) #define ALARM_INT2_STS (1 << 2) #define ALARM_INT1_STS (1 << 1) #define ALARM_INT0_STS (1 << 0) #define THS_FILTER 0x70 #define THS_CALIB0 0x74 #define THS_CALIB1 0x78 #define THS_DATA0 0x80 #define THS_DATA1 0x84 #define THS_DATA2 0x88 #define DATA_MASK 0xfff #define A83T_ADC_ACQUIRE_TIME 0x17 #define A83T_FILTER 0x4 #define A83T_INTC 0x1000 #define A83T_TEMP_BASE 2719000 +#define A83T_TEMP_MUL 1000 #define A83T_TEMP_DIV 14186 #define A83T_CLK_RATE 24000000 #define A64_ADC_ACQUIRE_TIME 0x190 #define A64_FILTER 0x6 -#define A64_INTC 0x18000 +#define A64_INTC 0x18000 #define A64_TEMP_BASE 2170000 +#define A64_TEMP_MUL 1000 #define A64_TEMP_DIV 8560 #define A64_CLK_RATE 4000000 +#define H3_ADC_ACQUIRE_TIME 0x3f +#define H3_FILTER 0x6 +#define H3_INTC 0x191000 +#define H3_TEMP_BASE 217000000 +#define H3_TEMP_MUL 121168 +#define H3_TEMP_DIV 1000000 +#define H3_CLK_RATE 4000000 + #define TEMP_C_TO_K 273 #define SENSOR_ENABLE_ALL (SENSOR0_EN|SENSOR1_EN|SENSOR2_EN) #define SHUT_INT_ALL (SHUT_INT0_STS|SHUT_INT1_STS|SHUT_INT2_STS) #define MAX_SENSORS 3 struct aw_thermal_sensor { const char *name; const char *desc; }; struct aw_thermal_config { struct aw_thermal_sensor sensors[MAX_SENSORS]; int nsensors; uint64_t clk_rate; uint32_t adc_acquire_time; uint32_t filter; uint32_t intc; - uint32_t temp_base; - uint32_t temp_div; + int temp_base; + int temp_mul; + int temp_div; + int calib; }; static const struct aw_thermal_config a83t_config = { .nsensors = 3, .sensors = { [0] = { .name = "cluster0", .desc = "CPU cluster 0 temperature", }, [1] = { .name = "cluster1", .desc = "CPU cluster 1 temperature", }, [2] = { .name = "gpu", .desc = "GPU temperature", }, }, .clk_rate = A83T_CLK_RATE, .adc_acquire_time = A83T_ADC_ACQUIRE_TIME, .filter = A83T_FILTER, .intc = A83T_INTC, .temp_base = A83T_TEMP_BASE, + .temp_mul = A83T_TEMP_MUL, .temp_div = A83T_TEMP_DIV, + .calib = 1, }; static const struct aw_thermal_config a64_config = { .nsensors = 3, .sensors = { [0] = { .name = "cpu", .desc = "CPU temperature", }, [1] = { .name = "gpu1", .desc = "GPU temperature 1", }, [2] = { .name = "gpu2", .desc = "GPU temperature 2", }, }, .clk_rate = A64_CLK_RATE, .adc_acquire_time = A64_ADC_ACQUIRE_TIME, .filter = A64_FILTER, .intc = A64_INTC, .temp_base = A64_TEMP_BASE, + .temp_mul = A64_TEMP_MUL, .temp_div = A64_TEMP_DIV, }; +static const struct aw_thermal_config h3_config = { + .nsensors = 1, + .sensors = { + [0] = { + .name = "cpu", + .desc = "CPU temperature", + }, + }, + .clk_rate = H3_CLK_RATE, + .adc_acquire_time = H3_ADC_ACQUIRE_TIME, + .filter = H3_FILTER, + .intc = H3_INTC, + .temp_base = H3_TEMP_BASE, + .temp_mul = H3_TEMP_MUL, + .temp_div = H3_TEMP_DIV, +}; + static struct ofw_compat_data compat_data[] = { { "allwinner,sun8i-a83t-ts", (uintptr_t)&a83t_config }, + { "allwinner,sun8i-h3-ts", (uintptr_t)&h3_config }, { "allwinner,sun50i-a64-ts", (uintptr_t)&a64_config }, { NULL, (uintptr_t)NULL } }; #define THS_CONF(d) \ (void *)ofw_bus_search_compatible((d), compat_data)->ocd_data struct aw_thermal_softc { struct resource *res[2]; struct aw_thermal_config *conf; }; static struct resource_spec aw_thermal_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; #define RD4(sc, reg) bus_read_4((sc)->res[0], (reg)) #define WR4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) static int aw_thermal_init(struct aw_thermal_softc *sc) { uint32_t calib0, calib1; int error; - /* Read calibration settings from SRAM */ - error = aw_sid_read_tscalib(&calib0, &calib1); - if (error != 0) - return (error); + if (sc->conf->calib) { + /* Read calibration settings from SRAM */ + error = aw_sid_read_tscalib(&calib0, &calib1); + if (error != 0) + return (error); - /* Write calibration settings to thermal controller */ - WR4(sc, THS_CALIB0, calib0); - WR4(sc, THS_CALIB1, calib1); + /* Write calibration settings to thermal controller */ + WR4(sc, THS_CALIB0, calib0); + WR4(sc, THS_CALIB1, calib1); + } /* Configure ADC acquire time (CLK_IN/(N+1)) and enable sensors */ WR4(sc, THS_CTRL1, ADC_CALI_EN); WR4(sc, THS_CTRL0, sc->conf->adc_acquire_time); WR4(sc, THS_CTRL2, sc->conf->adc_acquire_time << SENSOR_ACQ1_SHIFT); /* Enable average filter */ WR4(sc, THS_FILTER, sc->conf->filter); /* Enable interrupts */ WR4(sc, THS_INTS, RD4(sc, THS_INTS)); WR4(sc, THS_INTC, sc->conf->intc | SHUT_INT_ALL); /* Enable sensors */ WR4(sc, THS_CTRL2, RD4(sc, THS_CTRL2) | SENSOR_ENABLE_ALL); return (0); } static int aw_thermal_reg_to_temp(struct aw_thermal_softc *sc, uint32_t val) { - return ((sc->conf->temp_base - val * 1000) / sc->conf->temp_div); + return ((sc->conf->temp_base - (val * sc->conf->temp_mul)) / + sc->conf->temp_div); } static int aw_thermal_gettemp(struct aw_thermal_softc *sc, int sensor) { uint32_t val; val = RD4(sc, THS_DATA0 + (sensor * 4)); return (aw_thermal_reg_to_temp(sc, val) + TEMP_C_TO_K); } static int aw_thermal_sysctl(SYSCTL_HANDLER_ARGS) { struct aw_thermal_softc *sc; int sensor, val; sc = arg1; sensor = arg2; val = aw_thermal_gettemp(sc, sensor); return sysctl_handle_opaque(oidp, &val, sizeof(val), req); } static void aw_thermal_intr(void *arg) { struct aw_thermal_softc *sc; device_t dev; uint32_t ints; dev = arg; sc = device_get_softc(dev); ints = RD4(sc, THS_INTS); WR4(sc, THS_INTS, ints); if ((ints & SHUT_INT_ALL) != 0) { device_printf(dev, "WARNING - current temperature exceeds safe limits\n"); shutdown_nice(RB_POWEROFF); } } static int aw_thermal_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (THS_CONF(dev) == NULL) return (ENXIO); device_set_desc(dev, "Allwinner Thermal Sensor Controller"); return (BUS_PROBE_DEFAULT); } static int aw_thermal_attach(device_t dev) { struct aw_thermal_softc *sc; clk_t clk_ahb, clk_ths; hwreset_t rst; int i, error; void *ih; sc = device_get_softc(dev); clk_ahb = clk_ths = NULL; rst = NULL; ih = NULL; sc->conf = THS_CONF(dev); if (bus_alloc_resources(dev, aw_thermal_spec, sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); return (ENXIO); } if (clk_get_by_ofw_name(dev, 0, "ahb", &clk_ahb) == 0) { error = clk_enable(clk_ahb); if (error != 0) { device_printf(dev, "cannot enable ahb clock\n"); goto fail; } } if (clk_get_by_ofw_name(dev, 0, "ths", &clk_ths) == 0) { error = clk_set_freq(clk_ths, sc->conf->clk_rate, 0); if (error != 0) { device_printf(dev, "cannot set ths clock rate\n"); goto fail; } error = clk_enable(clk_ths); if (error != 0) { device_printf(dev, "cannot enable ths clock\n"); goto fail; } } if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) { error = hwreset_deassert(rst); if (error != 0) { device_printf(dev, "cannot de-assert reset\n"); goto fail; } } error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, NULL, aw_thermal_intr, dev, &ih); if (error != 0) { device_printf(dev, "cannot setup interrupt handler\n"); goto fail; } if (aw_thermal_init(sc) != 0) goto fail; for (i = 0; i < sc->conf->nsensors; i++) SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, sc->conf->sensors[i].name, CTLTYPE_INT | CTLFLAG_RD, sc, i, aw_thermal_sysctl, "IK0", sc->conf->sensors[i].desc); return (0); fail: if (ih != NULL) bus_teardown_intr(dev, sc->res[1], ih); if (rst != NULL) hwreset_release(rst); if (clk_ahb != NULL) clk_release(clk_ahb); if (clk_ths != NULL) clk_release(clk_ths); bus_release_resources(dev, aw_thermal_spec, sc->res); return (ENXIO); } static device_method_t aw_thermal_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_thermal_probe), DEVMETHOD(device_attach, aw_thermal_attach), DEVMETHOD_END }; static driver_t aw_thermal_driver = { "aw_thermal", aw_thermal_methods, sizeof(struct aw_thermal_softc), }; static devclass_t aw_thermal_devclass; DRIVER_MODULE(aw_thermal, simplebus, aw_thermal_driver, aw_thermal_devclass, 0, 0); MODULE_VERSION(aw_thermal, 1); Index: projects/clang390-import/sys/arm/allwinner/clk/aw_pll.c =================================================================== --- projects/clang390-import/sys/arm/allwinner/clk/aw_pll.c (revision 305430) +++ projects/clang390-import/sys/arm/allwinner/clk/aw_pll.c (revision 305431) @@ -1,997 +1,1067 @@ /*- * Copyright (c) 2016 Jared McNeill * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Allwinner PLL clock */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clkdev_if.h" #define AW_PLL_ENABLE (1 << 31) #define A10_PLL1_OUT_EXT_DIVP (0x3 << 16) #define A10_PLL1_OUT_EXT_DIVP_SHIFT 16 #define A10_PLL1_FACTOR_N (0x1f << 8) #define A10_PLL1_FACTOR_N_SHIFT 8 #define A10_PLL1_FACTOR_K (0x3 << 4) #define A10_PLL1_FACTOR_K_SHIFT 4 #define A10_PLL1_FACTOR_M (0x3 << 0) #define A10_PLL1_FACTOR_M_SHIFT 0 #define A10_PLL2_POST_DIV (0xf << 26) #define A10_PLL2_POST_DIV_SHIFT 26 #define A10_PLL2_FACTOR_N (0x7f << 8) #define A10_PLL2_FACTOR_N_SHIFT 8 #define A10_PLL2_PRE_DIV (0x1f << 0) #define A10_PLL2_PRE_DIV_SHIFT 0 #define A10_PLL3_MODE_SEL (0x1 << 15) #define A10_PLL3_MODE_SEL_FRACT (0 << 15) #define A10_PLL3_MODE_SEL_INT (1 << 15) #define A10_PLL3_FUNC_SET (0x1 << 14) #define A10_PLL3_FUNC_SET_270MHZ (0 << 14) #define A10_PLL3_FUNC_SET_297MHZ (1 << 14) #define A10_PLL3_FACTOR_M (0x7f << 0) #define A10_PLL3_FACTOR_M_SHIFT 0 #define A10_PLL3_REF_FREQ 3000000 #define A10_PLL5_OUT_EXT_DIVP (0x3 << 16) #define A10_PLL5_OUT_EXT_DIVP_SHIFT 16 #define A10_PLL5_FACTOR_N (0x1f << 8) #define A10_PLL5_FACTOR_N_SHIFT 8 #define A10_PLL5_FACTOR_K (0x3 << 4) #define A10_PLL5_FACTOR_K_SHIFT 4 #define A10_PLL5_FACTOR_M1 (0x3 << 2) #define A10_PLL5_FACTOR_M1_SHIFT 2 #define A10_PLL5_FACTOR_M (0x3 << 0) #define A10_PLL5_FACTOR_M_SHIFT 0 #define A10_PLL6_BYPASS_EN (1 << 30) #define A10_PLL6_SATA_CLK_EN (1 << 14) #define A10_PLL6_FACTOR_N (0x1f << 8) #define A10_PLL6_FACTOR_N_SHIFT 8 #define A10_PLL6_FACTOR_K (0x3 << 4) #define A10_PLL6_FACTOR_K_SHIFT 4 #define A10_PLL6_FACTOR_M (0x3 << 0) #define A10_PLL6_FACTOR_M_SHIFT 0 #define A10_PLL2_POST_DIV (0xf << 26) #define A13_PLL2_POST_DIV (0xf << 26) #define A13_PLL2_POST_DIV_SHIFT 26 #define A13_PLL2_FACTOR_N (0x7f << 8) #define A13_PLL2_FACTOR_N_SHIFT 8 #define A13_PLL2_PRE_DIV (0x1f << 0) #define A13_PLL2_PRE_DIV_SHIFT 0 #define A23_PLL1_FACTOR_P (0x3 << 16) #define A23_PLL1_FACTOR_P_SHIFT 16 #define A23_PLL1_FACTOR_N (0x1f << 8) #define A23_PLL1_FACTOR_N_SHIFT 8 #define A23_PLL1_FACTOR_K (0x3 << 4) #define A23_PLL1_FACTOR_K_SHIFT 4 #define A23_PLL1_FACTOR_M (0x3 << 0) #define A23_PLL1_FACTOR_M_SHIFT 0 #define A31_PLL1_LOCK (1 << 28) #define A31_PLL1_CPU_SIGMA_DELTA_EN (1 << 24) #define A31_PLL1_FACTOR_N (0x1f << 8) #define A31_PLL1_FACTOR_N_SHIFT 8 #define A31_PLL1_FACTOR_K (0x3 << 4) #define A31_PLL1_FACTOR_K_SHIFT 4 #define A31_PLL1_FACTOR_M (0x3 << 0) #define A31_PLL1_FACTOR_M_SHIFT 0 #define A31_PLL6_LOCK (1 << 28) #define A31_PLL6_BYPASS_EN (1 << 25) #define A31_PLL6_CLK_OUT_EN (1 << 24) #define A31_PLL6_24M_OUT_EN (1 << 18) #define A31_PLL6_24M_POST_DIV (0x3 << 16) #define A31_PLL6_24M_POST_DIV_SHIFT 16 #define A31_PLL6_FACTOR_N (0x1f << 8) #define A31_PLL6_FACTOR_N_SHIFT 8 #define A31_PLL6_FACTOR_K (0x3 << 4) #define A31_PLL6_FACTOR_K_SHIFT 4 #define A31_PLL6_DEFAULT_N 0x18 #define A31_PLL6_DEFAULT_K 0x1 #define A31_PLL6_TIMEOUT 10 #define A64_PLLHSIC_LOCK (1 << 28) #define A64_PLLHSIC_FRAC_CLK_OUT (1 << 25) #define A64_PLLHSIC_PLL_MODE_SEL (1 << 24) #define A64_PLLHSIC_PLL_SDM_EN (1 << 20) #define A64_PLLHSIC_FACTOR_N (0x7f << 8) #define A64_PLLHSIC_FACTOR_N_SHIFT 8 #define A64_PLLHSIC_PRE_DIV_M (0xf << 0) #define A64_PLLHSIC_PRE_DIV_M_SHIFT 0 #define A80_PLL4_CLK_OUT_EN (1 << 20) #define A80_PLL4_PLL_DIV2 (1 << 18) #define A80_PLL4_PLL_DIV1 (1 << 16) #define A80_PLL4_FACTOR_N (0xff << 8) #define A80_PLL4_FACTOR_N_SHIFT 8 #define CLKID_A10_PLL3_1X 0 #define CLKID_A10_PLL3_2X 1 #define CLKID_A10_PLL5_DDR 0 #define CLKID_A10_PLL5_OTHER 1 #define CLKID_A10_PLL6_SATA 0 #define CLKID_A10_PLL6_OTHER 1 #define CLKID_A10_PLL6 2 #define CLKID_A10_PLL6_DIV_4 3 #define CLKID_A31_PLL6 0 #define CLKID_A31_PLL6_X2 1 struct aw_pll_factor { unsigned int n; unsigned int k; unsigned int m; unsigned int p; uint64_t freq; }; #define PLLFACTOR(_n, _k, _m, _p, _freq) \ { .n = (_n), .k = (_k), .m = (_m), .p = (_p), .freq = (_freq) } static struct aw_pll_factor aw_a23_pll1_factors[] = { PLLFACTOR(16, 0, 0, 0, 408000000), PLLFACTOR(26, 0, 0, 0, 648000000), PLLFACTOR(16, 1, 0, 0, 816000000), PLLFACTOR(20, 1, 0, 0, 1008000000), PLLFACTOR(24, 1, 0, 0, 1200000000), + PLLFACTOR(26, 1, 0, 0, 1296000000), }; enum aw_pll_type { AWPLL_A10_PLL1 = 1, AWPLL_A10_PLL2, AWPLL_A10_PLL3, AWPLL_A10_PLL5, AWPLL_A10_PLL6, AWPLL_A13_PLL2, AWPLL_A23_PLL1, AWPLL_A31_PLL1, AWPLL_A31_PLL6, AWPLL_A64_PLLHSIC, AWPLL_A80_PLL4, + AWPLL_H3_PLL1, }; struct aw_pll_sc { enum aw_pll_type type; device_t clkdev; bus_addr_t reg; int id; }; struct aw_pll_funcs { int (*recalc)(struct aw_pll_sc *, uint64_t *); int (*set_freq)(struct aw_pll_sc *, uint64_t, uint64_t *, int); int (*init)(device_t, bus_addr_t, struct clknode_init_def *); }; #define PLL_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) #define PLL_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) #define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) #define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) static int a10_pll1_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m, n, k, p; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); p = 1 << ((val & A10_PLL1_OUT_EXT_DIVP) >> A10_PLL1_OUT_EXT_DIVP_SHIFT); m = ((val & A10_PLL1_FACTOR_M) >> A10_PLL1_FACTOR_M_SHIFT) + 1; k = ((val & A10_PLL1_FACTOR_K) >> A10_PLL1_FACTOR_K_SHIFT) + 1; n = (val & A10_PLL1_FACTOR_N) >> A10_PLL1_FACTOR_N_SHIFT; if (n == 0) n = 1; *freq = (*freq * n * k) / (m * p); return (0); } static int a10_pll2_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, post_div, n, pre_div; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); post_div = (val & A10_PLL2_POST_DIV) >> A10_PLL2_POST_DIV_SHIFT; if (post_div == 0) post_div = 1; n = (val & A10_PLL2_FACTOR_N) >> A10_PLL2_FACTOR_N_SHIFT; if (n == 0) n = 1; pre_div = (val & A10_PLL2_PRE_DIV) >> A10_PLL2_PRE_DIV_SHIFT; if (pre_div == 0) pre_div = 1; switch (sc->id) { case SUN4I_A10_PLL2_1X: *freq = (*freq * 2 * n) / pre_div / post_div / 2; break; case SUN4I_A10_PLL2_2X: *freq = (*freq * 2 * n) / pre_div / 4; break; case SUN4I_A10_PLL2_4X: *freq = (*freq * 2 * n) / pre_div / 2; break; case SUN4I_A10_PLL2_8X: *freq = (*freq * 2 * n) / pre_div; break; default: return (EINVAL); } return (0); } static int a10_pll2_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { uint32_t val, post_div, n, pre_div; if (sc->id != SUN4I_A10_PLL2_1X) return (ENXIO); /* * Audio Codec needs PLL2-1X to be either 24576000 or 22579200. * * PLL2-1X output frequency is (48MHz * n) / pre_div / post_div / 2. * To get as close as possible to the desired rate, we use a * pre-divider of 21 and a post-divider of 4. With these values, * a multiplier of 86 or 79 gets us close to the target rates. */ if (*fout != 24576000 && *fout != 22579200) return (EINVAL); pre_div = 21; post_div = 4; n = (*fout * pre_div * post_div * 2) / (2 * fin); DEVICE_LOCK(sc); PLL_READ(sc, &val); val &= ~(A10_PLL2_POST_DIV | A10_PLL2_FACTOR_N | A10_PLL2_PRE_DIV); val |= (post_div << A10_PLL2_POST_DIV_SHIFT); val |= (n << A10_PLL2_FACTOR_N_SHIFT); val |= (pre_div << A10_PLL2_PRE_DIV_SHIFT); PLL_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int a10_pll3_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); if ((val & A10_PLL3_MODE_SEL) == A10_PLL3_MODE_SEL_INT) { /* In integer mode, output is 3MHz * m */ m = (val & A10_PLL3_FACTOR_M) >> A10_PLL3_FACTOR_M_SHIFT; *freq = A10_PLL3_REF_FREQ * m; } else { /* In fractional mode, output is either 270MHz or 297MHz */ if ((val & A10_PLL3_FUNC_SET) == A10_PLL3_FUNC_SET_270MHZ) *freq = 270000000; else *freq = 297000000; } if (sc->id == CLKID_A10_PLL3_2X) *freq *= 2; return (0); } static int a10_pll3_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { uint32_t val, m, mode, func; m = *fout / A10_PLL3_REF_FREQ; if (sc->id == CLKID_A10_PLL3_2X) m /= 2; mode = A10_PLL3_MODE_SEL_INT; func = 0; *fout = m * A10_PLL3_REF_FREQ; if (sc->id == CLKID_A10_PLL3_2X) *fout *= 2; DEVICE_LOCK(sc); PLL_READ(sc, &val); val &= ~(A10_PLL3_MODE_SEL | A10_PLL3_FUNC_SET | A10_PLL3_FACTOR_M); val |= mode; val |= func; val |= (m << A10_PLL3_FACTOR_M_SHIFT); PLL_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int a10_pll3_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def) { uint32_t val; /* Allow changing PLL frequency while enabled */ def->flags = CLK_NODE_GLITCH_FREE; /* Set PLL to 297MHz */ CLKDEV_DEVICE_LOCK(dev); CLKDEV_READ_4(dev, reg, &val); val &= ~(A10_PLL3_MODE_SEL | A10_PLL3_FUNC_SET | A10_PLL3_FACTOR_M); val |= A10_PLL3_MODE_SEL_FRACT; val |= A10_PLL3_FUNC_SET_297MHZ; CLKDEV_WRITE_4(dev, reg, val); CLKDEV_DEVICE_UNLOCK(dev); return (0); } static int a10_pll5_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m, n, k, p; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); p = 1 << ((val & A10_PLL5_OUT_EXT_DIVP) >> A10_PLL5_OUT_EXT_DIVP_SHIFT); m = ((val & A10_PLL5_FACTOR_M) >> A10_PLL5_FACTOR_M_SHIFT) + 1; k = ((val & A10_PLL5_FACTOR_K) >> A10_PLL5_FACTOR_K_SHIFT) + 1; n = (val & A10_PLL5_FACTOR_N) >> A10_PLL5_FACTOR_N_SHIFT; if (n == 0) return (ENXIO); switch (sc->id) { case CLKID_A10_PLL5_DDR: *freq = (*freq * n * k) / m; break; case CLKID_A10_PLL5_OTHER: *freq = (*freq * n * k) / p; break; default: return (ENXIO); } return (0); } static int a10_pll6_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def) { uint32_t val, m, n, k; /* * SATA needs PLL6 to be a 100MHz clock. * * The SATA output frequency is (24MHz * n * k) / m / 6. * To get to 100MHz, k & m must be equal and n must be 25. */ m = k = 0; n = 25; CLKDEV_DEVICE_LOCK(dev); CLKDEV_READ_4(dev, reg, &val); val &= ~(A10_PLL6_FACTOR_N | A10_PLL6_FACTOR_K | A10_PLL6_FACTOR_M); val &= ~A10_PLL6_BYPASS_EN; val |= A10_PLL6_SATA_CLK_EN; val |= (n << A10_PLL6_FACTOR_N_SHIFT); val |= (k << A10_PLL6_FACTOR_K_SHIFT); val |= (m << A10_PLL6_FACTOR_M_SHIFT); CLKDEV_WRITE_4(dev, reg, val); CLKDEV_DEVICE_UNLOCK(dev); return (0); } static int a10_pll6_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m, k, n; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); m = ((val & A10_PLL6_FACTOR_M) >> A10_PLL6_FACTOR_M_SHIFT) + 1; k = ((val & A10_PLL6_FACTOR_K) >> A10_PLL6_FACTOR_K_SHIFT) + 1; n = (val & A10_PLL6_FACTOR_N) >> A10_PLL6_FACTOR_N_SHIFT; if (n == 0) return (ENXIO); switch (sc->id) { case CLKID_A10_PLL6_SATA: *freq = (*freq * n * k) / m / 6; break; case CLKID_A10_PLL6_OTHER: *freq = (*freq * n * k) / 2; break; case CLKID_A10_PLL6: *freq = (*freq * n * k); break; case CLKID_A10_PLL6_DIV_4: *freq = (*freq * n * k) / 4; break; default: return (ENXIO); } return (0); } static int a10_pll6_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { if (sc->id != CLKID_A10_PLL6_SATA) return (ENXIO); /* PLL6 SATA output has been set to 100MHz in a10_pll6_init */ if (*fout != 100000000) return (ERANGE); return (0); } static int a13_pll2_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, post_div, n, pre_div; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); post_div = ((val & A13_PLL2_POST_DIV) >> A13_PLL2_POST_DIV_SHIFT) + 1; if (post_div == 0) post_div = 1; n = (val & A13_PLL2_FACTOR_N) >> A13_PLL2_FACTOR_N_SHIFT; if (n == 0) n = 1; pre_div = ((val & A13_PLL2_PRE_DIV) >> A13_PLL2_PRE_DIV_SHIFT) + 1; if (pre_div == 0) pre_div = 1; switch (sc->id) { case SUN4I_A10_PLL2_1X: *freq = (*freq * 2 * n) / pre_div / post_div / 2; break; case SUN4I_A10_PLL2_2X: *freq = (*freq * 2 * n) / pre_div / 4; break; case SUN4I_A10_PLL2_4X: *freq = (*freq * 2 * n) / pre_div / 2; break; case SUN4I_A10_PLL2_8X: *freq = (*freq * 2 * n) / pre_div; break; default: return (EINVAL); } return (0); } static int a13_pll2_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { uint32_t val, post_div, n, pre_div; if (sc->id != SUN4I_A10_PLL2_1X) return (ENXIO); /* * Audio Codec needs PLL2-1X to be either 24576000 or 22579200. * * PLL2-1X output frequency is (48MHz * n) / pre_div / post_div / 2. * To get as close as possible to the desired rate, we use a * pre-divider of 21 and a post-divider of 4. With these values, * a multiplier of 86 or 79 gets us close to the target rates. */ if (*fout != 24576000 && *fout != 22579200) return (EINVAL); pre_div = 21; post_div = 4; n = (*fout * pre_div * post_div * 2) / (2 * fin); DEVICE_LOCK(sc); PLL_READ(sc, &val); val &= ~(A13_PLL2_POST_DIV | A13_PLL2_FACTOR_N | A13_PLL2_PRE_DIV); val |= ((post_div - 1) << A13_PLL2_POST_DIV_SHIFT); val |= (n << A13_PLL2_FACTOR_N_SHIFT); val |= ((pre_div - 1) << A13_PLL2_PRE_DIV_SHIFT); PLL_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int a23_pll1_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { struct aw_pll_factor *f; uint32_t val; int n; f = NULL; for (n = 0; n < nitems(aw_a23_pll1_factors); n++) { if (aw_a23_pll1_factors[n].freq == *fout) { f = &aw_a23_pll1_factors[n]; break; } } if (f == NULL) return (EINVAL); DEVICE_LOCK(sc); PLL_READ(sc, &val); val &= ~(A23_PLL1_FACTOR_N|A23_PLL1_FACTOR_K|A23_PLL1_FACTOR_M| A23_PLL1_FACTOR_P); val |= (f->n << A23_PLL1_FACTOR_N_SHIFT); val |= (f->k << A23_PLL1_FACTOR_K_SHIFT); val |= (f->m << A23_PLL1_FACTOR_M_SHIFT); val |= (f->p << A23_PLL1_FACTOR_P_SHIFT); PLL_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int a23_pll1_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m, n, k, p; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); m = ((val & A23_PLL1_FACTOR_M) >> A23_PLL1_FACTOR_M_SHIFT) + 1; k = ((val & A23_PLL1_FACTOR_K) >> A23_PLL1_FACTOR_K_SHIFT) + 1; n = ((val & A23_PLL1_FACTOR_N) >> A23_PLL1_FACTOR_N_SHIFT) + 1; p = ((val & A23_PLL1_FACTOR_P) >> A23_PLL1_FACTOR_P_SHIFT) + 1; *freq = (*freq * n * k) / (m * p); return (0); } static int +h3_pll1_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout, + int flags) +{ + struct aw_pll_factor *f; + uint32_t val, n, k, m, p; + int i; + + f = NULL; + for (i = 0; i < nitems(aw_a23_pll1_factors); i++) { + if (aw_a23_pll1_factors[i].freq == *fout) { + f = &aw_a23_pll1_factors[i]; + break; + } + } + if (f == NULL) + return (EINVAL); + + DEVICE_LOCK(sc); + PLL_READ(sc, &val); + + n = (val & A23_PLL1_FACTOR_N) >> A23_PLL1_FACTOR_N_SHIFT; + k = (val & A23_PLL1_FACTOR_K) >> A23_PLL1_FACTOR_K_SHIFT; + m = (val & A23_PLL1_FACTOR_M) >> A23_PLL1_FACTOR_M_SHIFT; + p = (val & A23_PLL1_FACTOR_P) >> A23_PLL1_FACTOR_P_SHIFT; + + if (p < f->p) { + val &= ~A23_PLL1_FACTOR_P; + val |= (f->p << A23_PLL1_FACTOR_P_SHIFT); + PLL_WRITE(sc, val); + DELAY(2000); + } + + if (m < f->m) { + val &= ~A23_PLL1_FACTOR_M; + val |= (f->m << A23_PLL1_FACTOR_M_SHIFT); + PLL_WRITE(sc, val); + DELAY(2000); + } + + val &= ~(A23_PLL1_FACTOR_N|A23_PLL1_FACTOR_K); + val |= (f->n << A23_PLL1_FACTOR_N_SHIFT); + val |= (f->k << A23_PLL1_FACTOR_K_SHIFT); + PLL_WRITE(sc, val); + DELAY(2000); + + if (m > f->m) { + val &= ~A23_PLL1_FACTOR_M; + val |= (f->m << A23_PLL1_FACTOR_M_SHIFT); + PLL_WRITE(sc, val); + DELAY(2000); + } + + if (p > f->p) { + val &= ~A23_PLL1_FACTOR_P; + val |= (f->p << A23_PLL1_FACTOR_P_SHIFT); + PLL_WRITE(sc, val); + DELAY(2000); + } + + DEVICE_UNLOCK(sc); + + return (0); + +} + +static int a31_pll1_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, m, n, k; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); m = ((val & A31_PLL1_FACTOR_M) >> A31_PLL1_FACTOR_M_SHIFT) + 1; k = ((val & A31_PLL1_FACTOR_K) >> A31_PLL1_FACTOR_K_SHIFT) + 1; n = ((val & A31_PLL1_FACTOR_N) >> A31_PLL1_FACTOR_N_SHIFT) + 1; *freq = (*freq * n * k) / m; return (0); } static int a31_pll6_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def) { uint32_t val; int retry; if (def->id != CLKID_A31_PLL6) return (0); /* * The datasheet recommends that PLL6 output should be fixed to * 600MHz. */ CLKDEV_DEVICE_LOCK(dev); CLKDEV_READ_4(dev, reg, &val); val &= ~(A31_PLL6_FACTOR_N | A31_PLL6_FACTOR_K | A31_PLL6_BYPASS_EN); val |= (A31_PLL6_DEFAULT_N << A31_PLL6_FACTOR_N_SHIFT); val |= (A31_PLL6_DEFAULT_K << A31_PLL6_FACTOR_K_SHIFT); val |= AW_PLL_ENABLE; CLKDEV_WRITE_4(dev, reg, val); /* Wait for PLL to become stable */ for (retry = A31_PLL6_TIMEOUT; retry > 0; retry--) { CLKDEV_READ_4(dev, reg, &val); if ((val & A31_PLL6_LOCK) == A31_PLL6_LOCK) break; DELAY(1); } CLKDEV_DEVICE_UNLOCK(dev); return (0); } static int a31_pll6_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, k, n; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); k = ((val & A10_PLL6_FACTOR_K) >> A10_PLL6_FACTOR_K_SHIFT) + 1; n = ((val & A10_PLL6_FACTOR_N) >> A10_PLL6_FACTOR_N_SHIFT) + 1; switch (sc->id) { case CLKID_A31_PLL6: *freq = (*freq * n * k) / 2; break; case CLKID_A31_PLL6_X2: *freq = *freq * n * k; break; default: return (ENXIO); } return (0); } static int a80_pll4_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, n, div1, div2; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); n = (val & A80_PLL4_FACTOR_N) >> A80_PLL4_FACTOR_N_SHIFT; div1 = (val & A80_PLL4_PLL_DIV1) == 0 ? 1 : 2; div2 = (val & A80_PLL4_PLL_DIV2) == 0 ? 1 : 2; *freq = (*freq * n) / div1 / div2; return (0); } static int a64_pllhsic_recalc(struct aw_pll_sc *sc, uint64_t *freq) { uint32_t val, n, m; DEVICE_LOCK(sc); PLL_READ(sc, &val); DEVICE_UNLOCK(sc); n = ((val & A64_PLLHSIC_FACTOR_N) >> A64_PLLHSIC_FACTOR_N_SHIFT) + 1; m = ((val & A64_PLLHSIC_PRE_DIV_M) >> A64_PLLHSIC_PRE_DIV_M_SHIFT) + 1; *freq = (*freq * n) / m; return (0); } static int a64_pllhsic_init(device_t dev, bus_addr_t reg, struct clknode_init_def *def) { uint32_t val; /* * PLL_HSIC default is 480MHz, just enable it. */ CLKDEV_DEVICE_LOCK(dev); CLKDEV_READ_4(dev, reg, &val); val |= AW_PLL_ENABLE; CLKDEV_WRITE_4(dev, reg, val); CLKDEV_DEVICE_UNLOCK(dev); return (0); } #define PLL(_type, _recalc, _set_freq, _init) \ [(_type)] = { \ .recalc = (_recalc), \ .set_freq = (_set_freq), \ .init = (_init) \ } static struct aw_pll_funcs aw_pll_func[] = { PLL(AWPLL_A10_PLL1, a10_pll1_recalc, NULL, NULL), PLL(AWPLL_A10_PLL2, a10_pll2_recalc, a10_pll2_set_freq, NULL), PLL(AWPLL_A10_PLL3, a10_pll3_recalc, a10_pll3_set_freq, a10_pll3_init), PLL(AWPLL_A10_PLL5, a10_pll5_recalc, NULL, NULL), PLL(AWPLL_A10_PLL6, a10_pll6_recalc, a10_pll6_set_freq, a10_pll6_init), PLL(AWPLL_A13_PLL2, a13_pll2_recalc, a13_pll2_set_freq, NULL), PLL(AWPLL_A23_PLL1, a23_pll1_recalc, a23_pll1_set_freq, NULL), PLL(AWPLL_A31_PLL1, a31_pll1_recalc, NULL, NULL), PLL(AWPLL_A31_PLL6, a31_pll6_recalc, NULL, a31_pll6_init), PLL(AWPLL_A80_PLL4, a80_pll4_recalc, NULL, NULL), PLL(AWPLL_A64_PLLHSIC, a64_pllhsic_recalc, NULL, a64_pllhsic_init), + PLL(AWPLL_H3_PLL1, a23_pll1_recalc, h3_pll1_set_freq, NULL), }; static struct ofw_compat_data compat_data[] = { { "allwinner,sun4i-a10-pll1-clk", AWPLL_A10_PLL1 }, { "allwinner,sun4i-a10-pll2-clk", AWPLL_A10_PLL2 }, { "allwinner,sun4i-a10-pll3-clk", AWPLL_A10_PLL3 }, { "allwinner,sun4i-a10-pll5-clk", AWPLL_A10_PLL5 }, { "allwinner,sun4i-a10-pll6-clk", AWPLL_A10_PLL6 }, { "allwinner,sun5i-a13-pll2-clk", AWPLL_A13_PLL2 }, { "allwinner,sun6i-a31-pll1-clk", AWPLL_A31_PLL1 }, { "allwinner,sun6i-a31-pll6-clk", AWPLL_A31_PLL6 }, { "allwinner,sun8i-a23-pll1-clk", AWPLL_A23_PLL1 }, + { "allwinner,sun8i-h3-pll1-clk", AWPLL_H3_PLL1 }, { "allwinner,sun9i-a80-pll4-clk", AWPLL_A80_PLL4 }, { "allwinner,sun50i-a64-pllhsic-clk", AWPLL_A64_PLLHSIC }, { NULL, 0 } }; static int aw_pll_init(struct clknode *clk, device_t dev) { clknode_init_parent_idx(clk, 0); return (0); } static int aw_pll_set_gate(struct clknode *clk, bool enable) { struct aw_pll_sc *sc; uint32_t val; sc = clknode_get_softc(clk); DEVICE_LOCK(sc); PLL_READ(sc, &val); if (enable) val |= AW_PLL_ENABLE; else val &= ~AW_PLL_ENABLE; PLL_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int aw_pll_recalc(struct clknode *clk, uint64_t *freq) { struct aw_pll_sc *sc; sc = clknode_get_softc(clk); if (aw_pll_func[sc->type].recalc == NULL) return (ENXIO); return (aw_pll_func[sc->type].recalc(sc, freq)); } static int aw_pll_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, int flags, int *stop) { struct aw_pll_sc *sc; sc = clknode_get_softc(clk); *stop = 1; if (aw_pll_func[sc->type].set_freq == NULL) return (ENXIO); return (aw_pll_func[sc->type].set_freq(sc, fin, fout, flags)); } static clknode_method_t aw_pll_clknode_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, aw_pll_init), CLKNODEMETHOD(clknode_set_gate, aw_pll_set_gate), CLKNODEMETHOD(clknode_recalc_freq, aw_pll_recalc), CLKNODEMETHOD(clknode_set_freq, aw_pll_set_freq), CLKNODEMETHOD_END }; DEFINE_CLASS_1(aw_pll_clknode, aw_pll_clknode_class, aw_pll_clknode_methods, sizeof(struct aw_pll_sc), clknode_class); static int aw_pll_create(device_t dev, bus_addr_t paddr, struct clkdom *clkdom, const char *pclkname, const char *clkname, int index) { enum aw_pll_type type; struct clknode_init_def clkdef; struct aw_pll_sc *sc; struct clknode *clk; int error; type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; memset(&clkdef, 0, sizeof(clkdef)); clkdef.id = index; clkdef.name = clkname; if (pclkname != NULL) { clkdef.parent_names = malloc(sizeof(char *), M_OFWPROP, M_WAITOK); clkdef.parent_names[0] = pclkname; clkdef.parent_cnt = 1; } else clkdef.parent_cnt = 0; if (aw_pll_func[type].init != NULL) { error = aw_pll_func[type].init(device_get_parent(dev), paddr, &clkdef); if (error != 0) { device_printf(dev, "clock %s init failed\n", clkname); return (error); } } clk = clknode_create(clkdom, &aw_pll_clknode_class, &clkdef); if (clk == NULL) { device_printf(dev, "cannot create clock node\n"); return (ENXIO); } sc = clknode_get_softc(clk); sc->clkdev = device_get_parent(dev); sc->reg = paddr; sc->type = type; sc->id = clkdef.id; clknode_register(clkdom, clk); OF_prop_free(__DECONST(char *, clkdef.parent_names)); return (0); } static int aw_pll_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner PLL Clock"); return (BUS_PROBE_DEFAULT); } static int aw_pll_attach(device_t dev) { struct clkdom *clkdom; const char **names; int index, nout, error; clk_t clk_parent; uint32_t *indices; bus_addr_t paddr; bus_size_t psize; phandle_t node; node = ofw_bus_get_node(dev); if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { device_printf(dev, "couldn't parse 'reg' property\n"); return (ENXIO); } clkdom = clkdom_create(dev); nout = clk_parse_ofw_out_names(dev, node, &names, &indices); if (nout == 0) { device_printf(dev, "no clock outputs found\n"); error = ENOENT; goto fail; } if (clk_get_by_ofw_index(dev, 0, 0, &clk_parent) != 0) clk_parent = NULL; for (index = 0; index < nout; index++) { error = aw_pll_create(dev, paddr, clkdom, clk_parent ? clk_get_name(clk_parent) : NULL, names[index], nout == 1 ? 1 : index); if (error) goto fail; } if (clkdom_finit(clkdom) != 0) { device_printf(dev, "cannot finalize clkdom initialization\n"); error = ENXIO; goto fail; } if (bootverbose) clkdom_dump(clkdom); return (0); fail: return (error); } static device_method_t aw_pll_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_pll_probe), DEVMETHOD(device_attach, aw_pll_attach), DEVMETHOD_END }; static driver_t aw_pll_driver = { "aw_pll", aw_pll_methods, 0, }; static devclass_t aw_pll_devclass; EARLY_DRIVER_MODULE(aw_pll, simplebus, aw_pll_driver, aw_pll_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Index: projects/clang390-import/sys/arm/allwinner/clk/aw_thsclk.c =================================================================== --- projects/clang390-import/sys/arm/allwinner/clk/aw_thsclk.c (revision 305430) +++ projects/clang390-import/sys/arm/allwinner/clk/aw_thsclk.c (revision 305431) @@ -1,320 +1,321 @@ /*- * Copyright (c) 2016 Jared McNeill * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Allwinner THS clocks */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "clkdev_if.h" #define SCLK_GATING (1 << 31) #define CLK_SRC_SEL (0x3 << 24) #define CLK_SRC_SEL_SHIFT 24 #define CLK_SRC_SEL_MAX 1 #define CLK_DIV_RATIO (0x3 << 0) #define CLK_DIV_RATIO_SHIFT 0 #define CLK_DIV_RATIO_MAX 3 static struct ofw_compat_data compat_data[] = { + { "allwinner,sun8i-h3-ths-clk", 1 }, { "allwinner,sun50i-a64-ths-clk", 1 }, { NULL, 0 } }; struct aw_thsclk_sc { device_t clkdev; bus_addr_t reg; }; #define THSCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) #define THSCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) #define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) #define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) static int aw_thsclk_init(struct clknode *clk, device_t dev) { struct aw_thsclk_sc *sc; uint32_t val, index; sc = clknode_get_softc(clk); DEVICE_LOCK(sc); THSCLK_READ(sc, &val); DEVICE_UNLOCK(sc); index = (val & CLK_SRC_SEL) >> CLK_SRC_SEL_SHIFT; clknode_init_parent_idx(clk, index); return (0); } static int aw_thsclk_set_mux(struct clknode *clk, int index) { struct aw_thsclk_sc *sc; uint32_t val; sc = clknode_get_softc(clk); if (index < 0 || index >= CLK_SRC_SEL_MAX) return (ERANGE); DEVICE_LOCK(sc); THSCLK_READ(sc, &val); val &= ~CLK_SRC_SEL; val |= (index << CLK_SRC_SEL_SHIFT); THSCLK_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int aw_thsclk_set_gate(struct clknode *clk, bool enable) { struct aw_thsclk_sc *sc; uint32_t val; sc = clknode_get_softc(clk); DEVICE_LOCK(sc); THSCLK_READ(sc, &val); if (enable) val |= SCLK_GATING; else val &= ~SCLK_GATING; THSCLK_WRITE(sc, val); DEVICE_UNLOCK(sc); return (0); } static int aw_thsclk_recalc_freq(struct clknode *clk, uint64_t *freq) { struct aw_thsclk_sc *sc; uint32_t val, div; sc = clknode_get_softc(clk); DEVICE_LOCK(sc); THSCLK_READ(sc, &val); DEVICE_UNLOCK(sc); switch (val & CLK_DIV_RATIO) { case 3: div = 6; break; default: div = 1 << (val & CLK_DIV_RATIO); break; } *freq = *freq / div; return (0); } static int aw_thsclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, int flags, int *stop) { struct aw_thsclk_sc *sc; uint32_t val, div, n, best_div, best_n; uint64_t cur_freq; int64_t best_diff, cur_diff; sc = clknode_get_softc(clk); best_diff = (int64_t)*fout; best_n = 0; for (div = 0; div <= CLK_DIV_RATIO_MAX; div++) { n = (div == 3) ? 6 : (1 << div); cur_freq = fin / n; cur_diff = (int64_t)*fout - cur_freq; if (cur_diff >= 0 && cur_diff < best_diff) { best_diff = cur_diff; best_div = div; best_n = n; } } if (best_diff == (int64_t)*fout || best_n == 0) return (ERANGE); DEVICE_LOCK(sc); THSCLK_READ(sc, &val); val &= ~CLK_DIV_RATIO; val |= (best_div << CLK_DIV_RATIO_SHIFT); THSCLK_WRITE(sc, val); DEVICE_UNLOCK(sc); *fout = fin / best_n; *stop = 1; return (0); } static clknode_method_t aw_thsclk_clknode_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, aw_thsclk_init), CLKNODEMETHOD(clknode_set_gate, aw_thsclk_set_gate), CLKNODEMETHOD(clknode_set_mux, aw_thsclk_set_mux), CLKNODEMETHOD(clknode_recalc_freq, aw_thsclk_recalc_freq), CLKNODEMETHOD(clknode_set_freq, aw_thsclk_set_freq), CLKNODEMETHOD_END }; DEFINE_CLASS_1(aw_thsclk_clknode, aw_thsclk_clknode_class, aw_thsclk_clknode_methods, sizeof(struct aw_thsclk_sc), clknode_class); static int aw_thsclk_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner THS Clock"); return (BUS_PROBE_DEFAULT); } static int aw_thsclk_attach(device_t dev) { struct clknode_init_def def; struct aw_thsclk_sc *sc; struct clkdom *clkdom; struct clknode *clk; clk_t clk_parent; bus_addr_t paddr; bus_size_t psize; phandle_t node; int error, ncells, i; node = ofw_bus_get_node(dev); if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { device_printf(dev, "cannot parse 'reg' property\n"); return (ENXIO); } error = ofw_bus_parse_xref_list_get_length(node, "clocks", "#clock-cells", &ncells); if (error != 0) { device_printf(dev, "cannot get clock count\n"); return (error); } clkdom = clkdom_create(dev); memset(&def, 0, sizeof(def)); error = clk_parse_ofw_clk_name(dev, node, &def.name); if (error != 0) { device_printf(dev, "cannot parse clock name\n"); error = ENXIO; goto fail; } def.id = 1; def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK); for (i = 0; i < ncells; i++) { error = clk_get_by_ofw_index(dev, 0, i, &clk_parent); if (error != 0) { device_printf(dev, "cannot get clock %d\n", i); goto fail; } def.parent_names[i] = clk_get_name(clk_parent); clk_release(clk_parent); } def.parent_cnt = ncells; clk = clknode_create(clkdom, &aw_thsclk_clknode_class, &def); if (clk == NULL) { device_printf(dev, "cannot create clknode\n"); error = ENXIO; goto fail; } sc = clknode_get_softc(clk); sc->reg = paddr; sc->clkdev = device_get_parent(dev); clknode_register(clkdom, clk); if (clkdom_finit(clkdom) != 0) { device_printf(dev, "cannot finalize clkdom initialization\n"); error = ENXIO; goto fail; } if (bootverbose) clkdom_dump(clkdom); return (0); fail: return (error); } static device_method_t aw_thsclk_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_thsclk_probe), DEVMETHOD(device_attach, aw_thsclk_attach), DEVMETHOD_END }; static driver_t aw_thsclk_driver = { "aw_thsclk", aw_thsclk_methods, 0 }; static devclass_t aw_thsclk_devclass; EARLY_DRIVER_MODULE(aw_thsclk, simplebus, aw_thsclk_driver, aw_thsclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Index: projects/clang390-import/sys/arm/allwinner/files.allwinner =================================================================== --- projects/clang390-import/sys/arm/allwinner/files.allwinner (revision 305430) +++ projects/clang390-import/sys/arm/allwinner/files.allwinner (revision 305431) @@ -1,55 +1,57 @@ # $FreeBSD$ kern/kern_clocksource.c standard arm/allwinner/a10_ahci.c optional ahci arm/allwinner/a10_codec.c optional sound arm/allwinner/a10_common.c standard arm/allwinner/a10_dmac.c standard arm/allwinner/a10_ehci.c optional ehci arm/allwinner/aw_usbphy.c optional ehci arm/allwinner/a10_gpio.c optional gpio arm/allwinner/a10_mmc.c optional mmc arm/allwinner/a10_sramc.c standard arm/allwinner/aw_nmi.c optional intrng arm/allwinner/aw_if_dwc.c optional dwc arm/allwinner/aw_rsb.c optional rsb arm/allwinner/aw_rtc.c standard arm/allwinner/aw_ts.c standard arm/allwinner/aw_wdog.c standard arm/allwinner/aw_machdep.c standard arm/allwinner/aw_mp.c optional smp arm/allwinner/axp209.c optional axp209 arm/allwinner/axp81x.c optional axp81x arm/allwinner/if_awg.c optional awg arm/allwinner/if_emac.c optional emac arm/allwinner/sunxi_dma_if.m standard dev/iicbus/twsi/a10_twsi.c optional twsi dev/usb/controller/generic_ohci.c optional ohci dev/usb/controller/generic_usb_if.m optional ohci arm/allwinner/aw_sid.c standard arm/allwinner/aw_thermal.c standard +dev/iicbus/sy8106a.c optional sy8106a #arm/allwinner/console.c standard arm/allwinner/a10_fb.c optional vt arm/allwinner/a10_hdmi.c optional hdmi arm/allwinner/a10_hdmiaudio.c optional hdmi sound arm/arm/hdmi_if.m optional hdmi arm/allwinner/aw_reset.c standard arm/allwinner/aw_ccu.c standard arm/allwinner/clk/aw_ahbclk.c standard arm/allwinner/clk/aw_apbclk.c standard arm/allwinner/clk/aw_axiclk.c standard arm/allwinner/clk/aw_codecclk.c standard arm/allwinner/clk/aw_cpuclk.c standard arm/allwinner/clk/aw_cpusclk.c standard arm/allwinner/clk/aw_debeclk.c standard arm/allwinner/clk/aw_gate.c standard arm/allwinner/clk/aw_gmacclk.c standard arm/allwinner/clk/aw_hdmiclk.c standard arm/allwinner/clk/aw_lcdclk.c standard arm/allwinner/clk/aw_modclk.c standard arm/allwinner/clk/aw_mmcclk.c standard arm/allwinner/clk/aw_oscclk.c standard arm/allwinner/clk/aw_pll.c standard +arm/allwinner/clk/aw_thsclk.c standard arm/allwinner/clk/aw_usbclk.c standard Index: projects/clang390-import/sys/arm/arm/minidump_machdep.c =================================================================== --- projects/clang390-import/sys/arm/arm/minidump_machdep.c (revision 305430) +++ projects/clang390-import/sys/arm/arm/minidump_machdep.c (revision 305431) @@ -1,395 +1,394 @@ /*- * Copyright (c) 2006 Peter Wemm * Copyright (c) 2008 Semihalf, Grzegorz Bernacki * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * from: FreeBSD: src/sys/i386/i386/minidump_machdep.c,v 1.6 2008/08/17 23:27:27 */ #include __FBSDID("$FreeBSD$"); #include "opt_watchdog.h" #include #include #include #include #include #include #include #ifdef SW_WATCHDOG #include #endif #include #include #include #include #include #include #include #include CTASSERT(sizeof(struct kerneldumpheader) == 512); /* * Don't touch the first SIZEOF_METADATA bytes on the dump device. This * is to protect us from metadata and to protect metadata from us. */ #define SIZEOF_METADATA (64*1024) uint32_t *vm_page_dump; int vm_page_dump_size; static struct kerneldumpheader kdh; static off_t dumplo; /* Handle chunked writes. */ static size_t fragsz; static void *dump_va; static uint64_t counter, progress; CTASSERT(sizeof(*vm_page_dump) == 4); static int is_dumpable(vm_paddr_t pa) { int i; for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) { if (pa >= dump_avail[i] && pa < dump_avail[i + 1]) return (1); } return (0); } #define PG2MB(pgs) (((pgs) + (1 << 8) - 1) >> 8) static int blk_flush(struct dumperinfo *di) { int error; if (fragsz == 0) return (0); error = dump_write(di, dump_va, 0, dumplo, fragsz); dumplo += fragsz; fragsz = 0; return (error); } static int blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz) { size_t len; int error, i, c; u_int maxdumpsz; maxdumpsz = min(di->maxiosize, MAXDUMPPGS * PAGE_SIZE); if (maxdumpsz == 0) /* seatbelt */ maxdumpsz = PAGE_SIZE; error = 0; if (ptr != NULL && pa != 0) { printf("cant have both va and pa!\n"); return (EINVAL); } if (pa != 0) { if ((sz % PAGE_SIZE) != 0) { printf("size not page aligned\n"); return (EINVAL); } if ((pa & PAGE_MASK) != 0) { printf("address not page aligned\n"); return (EINVAL); } } if (ptr != NULL) { /* Flush any pre-existing pa pages before a virtual dump. */ error = blk_flush(di); if (error) return (error); } while (sz) { len = maxdumpsz - fragsz; if (len > sz) len = sz; counter += len; progress -= len; if (counter >> 22) { printf(" %lld", PG2MB(progress >> PAGE_SHIFT)); counter &= (1<<22) - 1; } #ifdef SW_WATCHDOG wdog_kern_pat(WD_LASTVAL); #endif if (ptr) { error = dump_write(di, ptr, 0, dumplo, len); if (error) return (error); dumplo += len; ptr += len; sz -= len; } else { for (i = 0; i < len; i += PAGE_SIZE) dump_va = pmap_kenter_temporary(pa + i, (i + fragsz) >> PAGE_SHIFT); fragsz += len; pa += len; sz -= len; if (fragsz == maxdumpsz) { error = blk_flush(di); if (error) return (error); } } /* Check for user abort. */ c = cncheckc(); if (c == 0x03) return (ECANCELED); if (c != -1) printf(" (CTRL-C to abort) "); } return (0); } /* A buffer for general use. Its size must be one page at least. */ static char dumpbuf[PAGE_SIZE]; CTASSERT(sizeof(dumpbuf) % sizeof(pt2_entry_t) == 0); int minidumpsys(struct dumperinfo *di) { struct minidumphdr mdhdr; uint64_t dumpsize; uint32_t ptesize; uint32_t bits; uint32_t pa, prev_pa = 0, count = 0; vm_offset_t va; int i, bit, error; char *addr; /* * Flush caches. Note that in the SMP case this operates only on the * current CPU's L1 cache. Before we reach this point, code in either * the system shutdown or kernel debugger has called stop_cpus() to stop * all cores other than this one. Part of the ARM handling of * stop_cpus() is to call wbinv_all() on that core's local L1 cache. So * by time we get to here, all that remains is to flush the L1 for the * current CPU, then the L2. */ dcache_wbinv_poc_all(); counter = 0; /* Walk page table pages, set bits in vm_page_dump */ ptesize = 0; for (va = KERNBASE; va < kernel_vm_end; va += PAGE_SIZE) { pa = pmap_dump_kextract(va, NULL); if (pa != 0 && is_dumpable(pa)) dump_add_page(pa); ptesize += sizeof(pt2_entry_t); } /* Calculate dump size. */ dumpsize = ptesize; dumpsize += round_page(msgbufp->msg_size); dumpsize += round_page(vm_page_dump_size); for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { bits = vm_page_dump[i]; while (bits) { bit = ffs(bits) - 1; pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + bit) * PAGE_SIZE; /* Clear out undumpable pages now if needed */ if (is_dumpable(pa)) dumpsize += PAGE_SIZE; else dump_drop_page(pa); bits &= ~(1ul << bit); } } dumpsize += PAGE_SIZE; /* Determine dump offset on device. */ if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { error = ENOSPC; goto fail; } dumplo = di->mediaoffset + di->mediasize - dumpsize; dumplo -= sizeof(kdh) * 2; progress = dumpsize; /* Initialize mdhdr */ bzero(&mdhdr, sizeof(mdhdr)); strcpy(mdhdr.magic, MINIDUMP_MAGIC); mdhdr.version = MINIDUMP_VERSION; mdhdr.msgbufsize = msgbufp->msg_size; mdhdr.bitmapsize = vm_page_dump_size; mdhdr.ptesize = ptesize; mdhdr.kernbase = KERNBASE; mdhdr.arch = __ARM_ARCH; #if __ARM_ARCH >= 6 mdhdr.mmuformat = MINIDUMP_MMU_FORMAT_V6; #else mdhdr.mmuformat = MINIDUMP_MMU_FORMAT_V4; #endif mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARM_VERSION, dumpsize, di->blocksize); printf("Physical memory: %u MB\n", ptoa((uintmax_t)physmem) / 1048576); printf("Dumping %llu MB:", (long long)dumpsize >> 20); /* Dump leader */ error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); if (error) goto fail; dumplo += sizeof(kdh); /* Dump my header */ bzero(dumpbuf, sizeof(dumpbuf)); bcopy(&mdhdr, dumpbuf, sizeof(mdhdr)); error = blk_write(di, dumpbuf, 0, PAGE_SIZE); if (error) goto fail; /* Dump msgbuf up front */ error = blk_write(di, (char *)msgbufp->msg_ptr, 0, round_page(msgbufp->msg_size)); if (error) goto fail; /* Dump bitmap */ error = blk_write(di, (char *)vm_page_dump, 0, round_page(vm_page_dump_size)); if (error) goto fail; /* Dump kernel page table pages */ addr = dumpbuf; for (va = KERNBASE; va < kernel_vm_end; va += PAGE_SIZE) { pmap_dump_kextract(va, (pt2_entry_t *)addr); addr += sizeof(pt2_entry_t); if (addr == dumpbuf + sizeof(dumpbuf)) { error = blk_write(di, dumpbuf, 0, sizeof(dumpbuf)); if (error != 0) goto fail; addr = dumpbuf; } } if (addr != dumpbuf) { error = blk_write(di, dumpbuf, 0, addr - dumpbuf); if (error != 0) goto fail; } /* Dump memory chunks */ for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { bits = vm_page_dump[i]; while (bits) { bit = ffs(bits) - 1; pa = (((uint64_t)i * sizeof(*vm_page_dump) * NBBY) + bit) * PAGE_SIZE; if (!count) { prev_pa = pa; count++; } else { if (pa == (prev_pa + count * PAGE_SIZE)) count++; else { error = blk_write(di, NULL, prev_pa, count * PAGE_SIZE); if (error) goto fail; count = 1; prev_pa = pa; } } bits &= ~(1ul << bit); } } if (count) { error = blk_write(di, NULL, prev_pa, count * PAGE_SIZE); if (error) goto fail; count = 0; prev_pa = 0; } error = blk_flush(di); if (error) goto fail; /* Dump trailer */ error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); if (error) goto fail; dumplo += sizeof(kdh); /* Signal completion, signoff and exit stage left. */ dump_write(di, NULL, 0, 0, 0); printf("\nDump complete\n"); return (0); fail: if (error < 0) error = -error; if (error == ECANCELED) printf("\nDump aborted\n"); else if (error == ENOSPC) printf("\nDump failed. Partition too small.\n"); else printf("\n** DUMP FAILED (ERROR %d) **\n", error); return (error); - return (0); } void dump_add_page(vm_paddr_t pa) { int idx, bit; pa >>= PAGE_SHIFT; idx = pa >> 5; /* 2^5 = 32 */ bit = pa & 31; atomic_set_int(&vm_page_dump[idx], 1ul << bit); } void dump_drop_page(vm_paddr_t pa) { int idx, bit; pa >>= PAGE_SHIFT; idx = pa >> 5; /* 2^5 = 32 */ bit = pa & 31; atomic_clear_int(&vm_page_dump[idx], 1ul << bit); } Index: projects/clang390-import/sys/arm/conf/ALLWINNER =================================================================== --- projects/clang390-import/sys/arm/conf/ALLWINNER (revision 305430) +++ projects/clang390-import/sys/arm/conf/ALLWINNER (revision 305431) @@ -1,131 +1,132 @@ # # ALLWINNER -- Custom configuration for the Allwinner A20, A31, A31S, A83T, # and H3 ARM SoCs. # # For more information on this file, please read the config(5) manual page, # and/or the handbook section on Kernel Configuration Files: # # http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html # # The handbook is also available locally in /usr/share/doc/handbook # if you've installed the doc distribution, otherwise always see the # FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the # latest information. # # An exhaustive list of options and more detailed explanations of the # device lines is also present in the ../../conf/NOTES and NOTES files. # If you are in doubt as to the purpose or necessity of a line, check first # in NOTES. # # $FreeBSD$ ident ALLWINNER include "std.armv6" include "../allwinner/std.allwinner" options INTRNG options SOC_ALLWINNER_A20 options SOC_ALLWINNER_A31 options SOC_ALLWINNER_A31S options SOC_ALLWINNER_A83T options SOC_ALLWINNER_H3 options SCHED_ULE # ULE scheduler options SMP # Enable multiple cores options PLATFORM options PLATFORM_SMP options MULTIDELAY # NFS root from boopt/dhcp #options BOOTP #options BOOTP_NFSROOT #options BOOTP_COMPAT #options BOOTP_NFSV3 #options BOOTP_WIRED_TO=dwc0 # EXT_RESOURCES pseudo devices options EXT_RESOURCES device clk device phy device hwreset device regulator # Interrupt controller device gic # ARM Generic Timer device generic_timer # MMC/SD/SDIO Card slot support device mmc # mmc/sd bus device mmcsd # mmc/sd flash cards # ATA controllers device ahci # AHCI-compatible SATA controllers #device ata # Legacy ATA/SATA controllers # Console and misc device uart device uart_snps device pty device snp device md device random # Entropy device # I2C support device iicbus device iic device twsi device rsb device axp209 # AXP209 Power Management Unit device axp81x # AXP813/818 Power Management Unit +device sy8106a # SY8106A Buck Regulator # GPIO device gpio device gpioled device scbus # SCSI bus (required for ATA/SCSI) device da # Direct Access (disks) device pass # Passthrough device (direct ATA/SCSI access) # USB support options USB_HOST_ALIGN=64 # Align usb buffers to cache line size. device usb #device uhci device ohci device ehci device umass # Ethernet device loop device ether device mii device bpf #device emac # 10/100 integrated EMAC controller device dwc # 10/100/1000 integrated GMAC controller device awg # 10/100/1000 integrated EMAC controller # USB ethernet support, requires miibus device miibus # Sound support device sound # Framebuffer support device vt device kbdmux device ums device ukbd device videomode device hdmi # Pinmux device fdt_pinctrl # Flattened Device Tree options FDT # Configure using FDT/DTB data makeoptions MODULES_EXTRA=dtb/allwinner Index: projects/clang390-import/sys/boot/fdt/dts/arm64/a64.dtsi =================================================================== --- projects/clang390-import/sys/boot/fdt/dts/arm64/a64.dtsi (revision 305430) +++ projects/clang390-import/sys/boot/fdt/dts/arm64/a64.dtsi (revision 305431) @@ -1,189 +1,189 @@ /*- * Copyright (c) 2016 Jared McNeill * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ / { cpus { cpu@0 { clocks = <&cpu>; clock-latency = <2000000>; operating-points = < /* kHz uV */ 1200000 1300000 1008000 1200000 816000 1100000 648000 1040000 408000 1040000 >; }; }; clocks { pll_hsic: clk@01c20044 { #clock-cells = <0>; compatible = "allwinner,sun50i-a64-pllhsic-clk"; reg = <0x01c20044 0x4>; clocks = <&osc24M>; clock-output-names = "pll_hsic"; }; usb_clk: clk@01c200cc { #clock-cells = <1>; #reset-cells = <1>; compatible = "allwinner,sun8i-a83t-usb-clk"; reg = <0x01c200cc 0x4>; clocks = <&osc24M>, <&pll_hsic>; clock-indices = <8>, <9>, <10>, <11>, <16>, <17>; clock-output-names = "usb_phy0", "usb_phy1", "usb_hsic_pll", "usb_hsic_12m", "usb_otg_ohci", "usb_ohci0"; }; ths_clk: clk@01c20074 { #clock-cells = <0>; compatible = "allwinner,sun50i-a64-ths-clk"; reg = <0x01c20074 0x4>; clocks = <&osc24M>; clock-output-names = "ths"; }; }; soc { watchdog: watchdog@01c20ca0 { compatible = "allwinner,sun6i-a31-wdt"; reg = <0x01c20ca0 0x20>; interrupts = ; clocks = <&osc24M>; }; nmi_intc: interrupt-controller@01f00c0c { compatible = "allwinner,sun6i-a31-sc-nmi"; interrupt-controller; #interrupt-cells = <2>; reg = <0x01f00c0c 0x38>; interrupts = ; }; r_rsb: i2c@01f03400 { compatible = "allwinner,sun8i-a23-rsb"; reg = <0x01f03400 0x400>; interrupts = ; clock-frequency = <3000000>; pinctrl-names = "default"; pinctrl-0 = <&r_rsb_pins>; status = "disabled"; #address-cells = <1>; #size-cells = <0>; }; sid: eeprom@01c14000 { compatible = "allwinner,sun8i-a83t-sid"; reg = <0x01c14000 0x400>; }; rtp: rtp@01c25000 { compatible = "allwinner,sun50i-a64-ts"; reg = <0x01c25000 0x400>; - interrupts = ; + interrupts = ; clocks = <&bus_gates 72>, <&ths_clk>; clock-names = "ahb", "ths"; resets = <&ahb_rst 136>; #thermal-sensor-cells = <0>; }; usbphy: phy@01c19400 { compatible = "allwinner,sun50i-a64-usb-phy"; reg = <0x01c19400 0x24 0x01c1a800 0x4 0x01c1b800 0x4>; reg-names = "phy_ctrl", "pmu1", "pmu2"; clocks = <&usb_clk 8>, <&usb_clk 9>; clock-names = "usb0_phy", "usb1_phy"; resets = <&usb_clk 0>, <&usb_clk 1>; reset-names = "usb0_reset", "usb1_reset"; status = "disabled"; #phy-cells = <1>; }; ohci0: usb@01c1a400 { compatible = "generic-ohci"; reg = <0x01c1a400 0x100>; interrupts = ; clocks = <&bus_gates 28>, <&usb_clk 16>, <&usb_clk 17>; resets = <&ahb_rst 28>; phys = <&usbphy 1>; phy-names = "usb"; status = "disabled"; }; ehci0: usb@01c1a000 { compatible = "allwinner,sun8i-a83t-ehci", "generic-ehci"; reg = <0x01c1a000 0x100>; interrupts = ; clocks = <&bus_gates 24>; resets = <&ahb_rst 24>; phys = <&usbphy 1>; phy-names = "usb"; status = "disabled"; }; ohci1: usb@01c1b400 { compatible = "generic-ohci"; reg = <0x01c1b400 0x100>; interrupts = ; clocks = <&bus_gates 29>, <&usb_clk 16>, <&usb_clk 17>; resets = <&ahb_rst 29>; phys = <&usbphy 2>; phy-names = "usb"; status = "disabled"; }; ehci1: usb@01c1b000 { compatible = "allwinner,sun8i-a83t-ehci", "generic-ehci"; reg = <0x01c1b000 0x100>; interrupts = ; clocks = <&bus_gates 25>; resets = <&ahb_rst 25>; phys = <&usbphy 2>; phy-names = "usb"; status = "disabled"; }; }; }; &pio { r_rsb_pins: r_rsb { allwinner,pins = "PL0", "PL1"; allwinner,function = "s_rsb"; allwinner,drive = ; allwinner,pull = ; }; }; Index: projects/clang390-import/sys/dev/bhnd/cores/usb/bhnd_ehci.c =================================================================== --- projects/clang390-import/sys/dev/bhnd/cores/usb/bhnd_ehci.c (nonexistent) +++ projects/clang390-import/sys/dev/bhnd/cores/usb/bhnd_ehci.c (revision 305431) @@ -0,0 +1,267 @@ +/*- + * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. + * Copyright (c) 2010, Aleksandr Rybalko + * All rights reserved. + * + * Developed by Semihalf. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of MARVELL nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * BHND attachment driver for the USB Enhanced Host Controller. + * Ported from ZRouter with insignificant adaptations for FreeBSD11. + */ + +#include "opt_bus.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define EHCI_HC_DEVSTR "Broadcom EHCI" + +#define USB_BRIDGE_INTR_CAUSE 0x210 +#define USB_BRIDGE_INTR_MASK 0x214 + +static device_attach_t bhnd_ehci_attach; +static device_detach_t bhnd_ehci_detach; + +static int bhnd_ehci_probe(device_t self); +static void bhnd_ehci_post_reset(struct ehci_softc *ehci_softc); + +static int +bhnd_ehci_probe(device_t self) +{ + + device_set_desc(self, EHCI_HC_DEVSTR); + + return (BUS_PROBE_DEFAULT); +} + +static void +bhnd_ehci_post_reset(struct ehci_softc *ehci_softc) +{ + uint32_t usbmode; + + /* Force HOST mode */ + usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM); + usbmode &= ~EHCI_UM_CM; + usbmode |= EHCI_UM_CM_HOST; + EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode); +} + +static int +bhnd_ehci_attach(device_t self) +{ + ehci_softc_t *sc; + int err; + int rid; + + sc = device_get_softc(self); + /* initialise some bus fields */ + sc->sc_bus.parent = self; + sc->sc_bus.devices = sc->sc_devices; + sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + sc->sc_bus.usbrev = USB_REV_2_0; + sc->sc_bus.dma_bits = 32; + + /* get all DMA memory */ + if ((err = usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), + &ehci_iterate_hw_softc)) != 0) { + BHND_ERROR_DEV(self, "can't allocate DMA memory: %d", err); + return (ENOMEM); + } + + rid = 0; + sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (!sc->sc_io_res) { + BHND_ERROR_DEV(self, "Could not map memory"); + goto error; + } + sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); + sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); + sc->sc_io_size = rman_get_size(sc->sc_io_res); + + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->sc_irq_res == NULL) { + BHND_ERROR_DEV(self, "Could not allocate error irq"); + bhnd_ehci_detach(self); + return (ENXIO); + } + + sc->sc_bus.bdev = device_add_child(self, "usbus", -1); + if (!sc->sc_bus.bdev) { + BHND_ERROR_DEV(self, "Could not add USB device"); + goto error; + } + device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); + device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); + + sprintf(sc->sc_vendor, "Broadcom"); + + err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); + if (err) { + BHND_ERROR_DEV(self, "Could not setup irq, %d", err); + sc->sc_intr_hdl = NULL; + goto error; + } + + sc->sc_flags |= EHCI_SCFLG_LOSTINTRBUG; + sc->sc_vendor_post_reset = bhnd_ehci_post_reset; + + err = ehci_init(sc); + if (!err) { + err = device_probe_and_attach(sc->sc_bus.bdev); + } + if (err) { + BHND_ERROR_DEV(self, "USB init failed err=%d", err); + goto error; + } + return (0); + +error: + bhnd_ehci_detach(self); + return (ENXIO); +} + +static int +bhnd_ehci_detach(device_t self) +{ + ehci_softc_t *sc; + device_t bdev; + int err; + + sc = device_get_softc(self); + + if (sc->sc_bus.bdev) { + bdev = sc->sc_bus.bdev; + device_detach(bdev); + device_delete_child(self, bdev); + } + /* during module unload there are lots of children leftover */ + device_delete_children(self); + + /* + * disable interrupts that might have been switched on in ehci_init + */ +#ifdef notyet + if (sc->sc_io_res) { + EWRITE4(sc, EHCI_USBINTR, 0); + EWRITE4(sc, USB_BRIDGE_INTR_MASK, 0); + } +#endif + if (sc->sc_irq_res && sc->sc_intr_hdl) { + /* + * only call ehci_detach() after ehci_init() + */ + ehci_detach(sc); + + err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); + + if (err) + /* XXX or should we panic? */ + BHND_ERROR_DEV(self, "Could not tear down irq, %d", err); + + sc->sc_intr_hdl = NULL; + } + if (sc->sc_irq_res) { + bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); + sc->sc_irq_res = NULL; + } + if (sc->sc_io_res) { + bus_release_resource(self, SYS_RES_MEMORY, 0, sc->sc_io_res); + sc->sc_io_res = NULL; + } + usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); + + return (0); +} + +static device_method_t ehci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, bhnd_ehci_probe), + DEVMETHOD(device_attach, bhnd_ehci_attach), + DEVMETHOD(device_detach, bhnd_ehci_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + {0, 0} +}; + +static driver_t ehci_driver = { + "ehci", + ehci_methods, + sizeof(ehci_softc_t), +}; + +static devclass_t ehci_devclass; + +DRIVER_MODULE(ehci, bhnd_usb, ehci_driver, ehci_devclass, 0, 0); +MODULE_DEPEND(ehci, usb, 1, 1, 1); Property changes on: projects/clang390-import/sys/dev/bhnd/cores/usb/bhnd_ehci.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang390-import/sys/dev/bhnd/cores/usb/bhnd_ohci.c =================================================================== --- projects/clang390-import/sys/dev/bhnd/cores/usb/bhnd_ohci.c (nonexistent) +++ projects/clang390-import/sys/dev/bhnd/cores/usb/bhnd_ohci.c (revision 305431) @@ -0,0 +1,228 @@ +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * Copyright (c) 2010, Aleksandr Rybalko + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (augustss@carlstedt.se) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * USB Open Host Controller driver. + * + * OHCI spec: http://www.intel.com/design/usb/ohci11d.pdf + */ + +/* The low level controller code for OHCI has been split into + * SIBA probes and OHCI specific code. This was done to facilitate the + * sharing of code between *BSD's + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +static device_probe_t bhnd_ohci_probe; +static device_attach_t bhnd_ohci_attach; +static device_detach_t bhnd_ohci_detach; + +static int +bhnd_ohci_probe(device_t self) +{ + device_set_desc(self, "Broadcom OHCI"); + return (0); +} + +static int +bhnd_ohci_attach(device_t self) +{ + ohci_softc_t *sc; + int rid; + int err; + + sc = device_get_softc(self); + /* initialise some bus fields */ + sc->sc_bus.parent = self; + sc->sc_bus.devices = sc->sc_devices; + sc->sc_bus.devices_max = OHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; + + /* get all DMA memory */ + if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), + &ohci_iterate_hw_softc)) { + return (ENOMEM); + } + sc->sc_dev = self; + + rid = 0; + sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (!sc->sc_io_res) { + device_printf(self, "Could not map memory\n"); + goto error; + } + sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); + sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); + sc->sc_io_size = rman_get_size(sc->sc_io_res); + + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + if (sc->sc_irq_res == NULL) { + device_printf(self, "Could not allocate irq\n"); + goto error; + } + sc->sc_bus.bdev = device_add_child(self, "usbus", -1); + if (!sc->sc_bus.bdev) { + device_printf(self, "Could not add USB device\n"); + goto error; + } + device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); + + strlcpy(sc->sc_vendor, "Broadcom", sizeof(sc->sc_vendor)); + + err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (driver_intr_t *)ohci_interrupt, sc, &sc->sc_intr_hdl); + + if (err) { + device_printf(self, "Could not setup irq, %d\n", err); + sc->sc_intr_hdl = NULL; + goto error; + } + err = ohci_init(sc); + if (!err) { + err = device_probe_and_attach(sc->sc_bus.bdev); + } + if (err) { + device_printf(self, "USB init failed\n"); + goto error; + } + return (0); + +error: + bhnd_ohci_detach(self); + return (ENXIO); +} + +static int +bhnd_ohci_detach(device_t self) +{ + ohci_softc_t *sc; + device_t bdev; + + sc = device_get_softc(self); + + if (sc->sc_bus.bdev) { + bdev = sc->sc_bus.bdev; + device_detach(bdev); + device_delete_child(self, bdev); + } + /* during module unload there are lots of children leftover */ + device_delete_children(self); + + if (sc->sc_irq_res && sc->sc_intr_hdl) { + /* + * only call ohci_detach() after ohci_init() + */ + ohci_detach(sc); + + int err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); + + if (err) { + /* XXX or should we panic? */ + device_printf(self, "Could not tear down irq, %d\n", + err); + } + sc->sc_intr_hdl = NULL; + } + if (sc->sc_irq_res) { + bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); + sc->sc_irq_res = NULL; + } + if (sc->sc_io_res) { + bus_release_resource(self, SYS_RES_MEMORY, 0, + sc->sc_io_res); + sc->sc_io_res = NULL; + } + usb_bus_mem_free_all(&sc->sc_bus, &ohci_iterate_hw_softc); + + return (0); +} + +static device_method_t bhnd_ohci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, bhnd_ohci_probe), + DEVMETHOD(device_attach, bhnd_ohci_attach), + DEVMETHOD(device_detach, bhnd_ohci_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + DEVMETHOD_END +}; + +static driver_t ohci_driver = { + .name = "ohci", + .methods = bhnd_ohci_methods, + .size = sizeof(struct ohci_softc), +}; + +static devclass_t ohci_devclass; + +DRIVER_MODULE(ohci, bhnd_usb, ohci_driver, ohci_devclass, 0, 0); +MODULE_DEPEND(ohci, usb, 1, 1, 1); Property changes on: projects/clang390-import/sys/dev/bhnd/cores/usb/bhnd_ohci.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang390-import/sys/dev/bhnd/cores/usb/bhnd_usb.c =================================================================== --- projects/clang390-import/sys/dev/bhnd/cores/usb/bhnd_usb.c (nonexistent) +++ projects/clang390-import/sys/dev/bhnd/cores/usb/bhnd_usb.c (revision 305431) @@ -0,0 +1,487 @@ +/*- + * Copyright (c) 2010, Aleksandr Rybalko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * Ported version of BroadCom USB core driver from ZRouter project + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include "bhnd_usbvar.h" + +/****************************** Variables ************************************/ +static const struct bhnd_device bhnd_usb_devs[] = { + BHND_DEVICE(BCM, USB, "USB1.1 Host/Device core", NULL), + BHND_DEVICE(BCM, USB20H, "USB2.0 Host core", NULL), + BHND_DEVICE(BCM, USB20D, "USB2.0 Device core", NULL), + BHND_DEVICE(BCM, USB11H, "USB1.1 Host core", NULL), + BHND_DEVICE(BCM, USB11D, "USB1.1 Device core", NULL), + BHND_DEVICE_END +}; + +/****************************** Prototypes ***********************************/ + +static int bhnd_usb_attach(device_t); +static int bhnd_usb_probe(device_t); +static device_t bhnd_usb_add_child(device_t dev, u_int order, const char *name, + int unit); +static int bhnd_usb_print_all_resources(device_t dev); +static int bhnd_usb_print_child(device_t bus, device_t child); + +static struct resource * bhnd_usb_alloc_resource(device_t bus, + device_t child, int type, int *rid, + rman_res_t start, rman_res_t end, + rman_res_t count, u_int flags); +static int bhnd_usb_release_resource(device_t dev, + device_t child, int type, int rid, + struct resource *r); + +static struct resource_list * bhnd_usb_get_reslist(device_t dev, + device_t child); + +static int +bhnd_usb_probe(device_t dev) +{ + const struct bhnd_device *id; + + id = bhnd_device_lookup(dev, bhnd_usb_devs, sizeof(bhnd_usb_devs[0])); + if (id == NULL) + return (ENXIO); + + device_set_desc(dev, id->desc); + return (BUS_PROBE_DEFAULT); +} + +static int +bhnd_usb_attach(device_t dev) +{ + struct bhnd_usb_softc *sc; + int rid; + uint32_t tmp; + int tries, err; + + sc = device_get_softc(dev); + + BHND_BUS_RESET_CORE(device_get_parent(dev), dev, 0); + + /* + * Allocate the resources which the parent bus has already + * determined for us. + * XXX: There are few windows (usually 2), RID should be chip-specific + */ + rid = 0; + sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (sc->sc_mem == NULL) { + BHND_ERROR_DEV(dev, "unable to allocate memory"); + return (ENXIO); + } + + sc->sc_bt = rman_get_bustag(sc->sc_mem); + sc->sc_bh = rman_get_bushandle(sc->sc_mem); + sc->sc_maddr = rman_get_start(sc->sc_mem); + sc->sc_msize = rman_get_size(sc->sc_mem); + + rid = 0; + sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + if (sc->sc_irq == NULL) { + BHND_ERROR_DEV(dev, "unable to allocate IRQ"); + return (ENXIO); + } + + sc->sc_irqn = rman_get_start(sc->sc_irq); + + sc->mem_rman.rm_start = sc->sc_maddr; + sc->mem_rman.rm_end = sc->sc_maddr + sc->sc_msize - 1; + sc->mem_rman.rm_type = RMAN_ARRAY; + sc->mem_rman.rm_descr = "BHND USB core I/O memory addresses"; + if (rman_init(&sc->mem_rman) != 0 || + rman_manage_region(&sc->mem_rman, sc->mem_rman.rm_start, + sc->mem_rman.rm_end) != 0) { + panic("%s: sc->mem_rman", __func__); + } + + sc->irq_rman.rm_start = sc->sc_irqn; + sc->irq_rman.rm_end = sc->sc_irqn; + sc->irq_rman.rm_type = RMAN_ARRAY; + sc->irq_rman.rm_descr = "BHND USB core IRQ"; + /* + * BHND USB share same IRQ between OHCI and EHCI + */ + if (rman_init(&sc->irq_rman) != 0 || + rman_manage_region(&sc->irq_rman, sc->irq_rman.rm_start, + sc->irq_rman.rm_end) != 0) + panic("%s: failed to set up IRQ rman", __func__); + + /* TODO: macros for registers */ + bus_write_4(sc->sc_mem, 0x200, 0x7ff); + DELAY(100); + +#define OHCI_CONTROL 0x04 + bus_write_4(sc->sc_mem, OHCI_CONTROL, 0); + + if ( bhnd_get_device(dev) == BHND_COREID_USB20H) { + + uint32_t rev; + BHND_INFO_DEV(dev, "USB HOST 2.0 setup for rev %d", rev); + rev = bhnd_get_hwrev(dev); + if (rev == 1/* ? == 2 */) { + /* SiBa code */ + + /* Change Flush control reg */ + tmp = bus_read_4(sc->sc_mem, 0x400) & ~0x8; + bus_write_4(sc->sc_mem, 0x400, tmp); + tmp = bus_read_4(sc->sc_mem, 0x400); + BHND_DEBUG_DEV(dev, "USB20H fcr: 0x%x", tmp); + + /* Change Shim control reg */ + tmp = bus_read_4(sc->sc_mem, 0x304) & ~0x100; + bus_write_4(sc->sc_mem, 0x304, tmp); + tmp = bus_read_4(sc->sc_mem, 0x304); + BHND_DEBUG_DEV(dev, "USB20H shim: 0x%x", tmp); + } else if (rev >= 5) { + /* BCMA code */ + err = bhnd_alloc_pmu(dev); + if(err) { + BHND_ERROR_DEV(dev, "can't alloc pmu: %d", err); + return (err); + } + + err = bhnd_request_ext_rsrc(dev, 1); + if(err) { + BHND_ERROR_DEV(dev, "can't req ext: %d", err); + return (err); + } + /* Take out of resets */ + bus_write_4(sc->sc_mem, 0x200, 0x4ff); + DELAY(25); + bus_write_4(sc->sc_mem, 0x200, 0x6ff); + DELAY(25); + + /* Make sure digital and AFE are locked in USB PHY */ + bus_write_4(sc->sc_mem, 0x524, 0x6b); + DELAY(50); + bus_read_4(sc->sc_mem, 0x524); + DELAY(50); + bus_write_4(sc->sc_mem, 0x524, 0xab); + DELAY(50); + bus_read_4(sc->sc_mem, 0x524); + DELAY(50); + bus_write_4(sc->sc_mem, 0x524, 0x2b); + DELAY(50); + bus_read_4(sc->sc_mem, 0x524); + DELAY(50); + bus_write_4(sc->sc_mem, 0x524, 0x10ab); + DELAY(50); + bus_read_4(sc->sc_mem, 0x524); + + tries = 10000; + for (;;) { + DELAY(10); + tmp = bus_read_4(sc->sc_mem, 0x528); + if (tmp & 0xc000) + break; + if (--tries != 0) + continue; + + tmp = bus_read_4(sc->sc_mem, 0x528); + BHND_ERROR_DEV(dev, "USB20H mdio_rddata 0x%08x", tmp); + } + + /* XXX: Puzzle code */ + bus_write_4(sc->sc_mem, 0x528, 0x80000000); + bus_read_4(sc->sc_mem, 0x314); + DELAY(265); + bus_write_4(sc->sc_mem, 0x200, 0x7ff); + DELAY(10); + + /* Take USB and HSIC out of non-driving modes */ + bus_write_4(sc->sc_mem, 0x510, 0); + } + } + + bus_generic_probe(dev); + + if (bhnd_get_device(dev) == BHND_COREID_USB20H && + ( bhnd_get_hwrev(dev) > 0)) + bhnd_usb_add_child(dev, 0, "ehci", -1); + bhnd_usb_add_child(dev, 1, "ohci", -1); + + bus_generic_attach(dev); + + return (0); +} + +static struct resource * +bhnd_usb_alloc_resource(device_t bus, device_t child, int type, int *rid, + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) +{ + struct resource *rv; + struct resource_list *rl; + struct resource_list_entry *rle; + int isdefault, needactivate; + struct bhnd_usb_softc *sc = device_get_softc(bus); + + isdefault = RMAN_IS_DEFAULT_RANGE(start,end); + needactivate = flags & RF_ACTIVE; + rl = BUS_GET_RESOURCE_LIST(bus, child); + rle = NULL; + + if (isdefault) { + BHND_INFO_DEV(bus, "trying allocate def %d - %d for %s", type, + *rid, device_get_nameunit(child) ); + rle = resource_list_find(rl, type, *rid); + if (rle == NULL) + return (NULL); + if (rle->res != NULL) + panic("%s: resource entry is busy", __func__); + start = rle->start; + end = rle->end; + count = rle->count; + } else { + BHND_INFO_DEV(bus, "trying allocate %d - %d (%jx-%jx) for %s", type, + *rid, start, end, device_get_nameunit(child) ); + } + + /* + * If the request is for a resource which we manage, + * attempt to satisfy the allocation ourselves. + */ + if (type == SYS_RES_MEMORY) { + + rv = rman_reserve_resource(&sc->mem_rman, start, end, count, + flags, child); + if (rv == 0) { + BHND_ERROR_DEV(bus, "could not reserve resource"); + return (0); + } + + rman_set_rid(rv, *rid); + + if (needactivate && + bus_activate_resource(child, type, *rid, rv)) { + BHND_ERROR_DEV(bus, "could not activate resource"); + rman_release_resource(rv); + return (0); + } + + return (rv); + } + + if (type == SYS_RES_IRQ) { + + rv = rman_reserve_resource(&sc->irq_rman, start, end, count, + flags, child); + if (rv == 0) { + BHND_ERROR_DEV(bus, "could not reserve resource"); + return (0); + } + + rman_set_rid(rv, *rid); + + if (needactivate && + bus_activate_resource(child, type, *rid, rv)) { + BHND_ERROR_DEV(bus, "could not activate resource"); + rman_release_resource(rv); + return (0); + } + + return (rv); + } + + /* + * Pass the request to the parent. + */ + return (resource_list_alloc(rl, bus, child, type, rid, + start, end, count, flags)); +} + +static struct resource_list * +bhnd_usb_get_reslist(device_t dev, device_t child) +{ + struct bhnd_usb_devinfo *sdi; + + sdi = device_get_ivars(child); + + return (&sdi->sdi_rl); +} + +static int +bhnd_usb_release_resource(device_t dev, device_t child, int type, + int rid, struct resource *r) +{ + struct resource_list *rl; + struct resource_list_entry *rle; + + rl = bhnd_usb_get_reslist(dev, child); + if (rl == NULL) + return (EINVAL); + rle = resource_list_find(rl, type, rid); + if (rle == NULL) + return (EINVAL); + rman_release_resource(r); + rle->res = NULL; + + return (0); +} + +static int +bhnd_usb_print_all_resources(device_t dev) +{ + struct bhnd_usb_devinfo *sdi; + struct resource_list *rl; + int retval; + + retval = 0; + sdi = device_get_ivars(dev); + rl = &sdi->sdi_rl; + + if (STAILQ_FIRST(rl)) + retval += printf(" at"); + + retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%jx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + + return (retval); +} + +static int +bhnd_usb_print_child(device_t bus, device_t child) +{ + int retval = 0; + + retval += bus_print_child_header(bus, child); + retval += bhnd_usb_print_all_resources(child); + if (device_get_flags(child)) + retval += printf(" flags %#x", device_get_flags(child)); + retval += printf(" on %s\n", device_get_nameunit(bus)); + + return (retval); +} + +static device_t +bhnd_usb_add_child(device_t dev, u_int order, const char *name, int unit) +{ + struct bhnd_usb_softc *sc; + struct bhnd_usb_devinfo *sdi; + device_t child; + + sc = device_get_softc(dev); + child = device_add_child_ordered(dev, order, name, unit); + + if (child == NULL) + return (NULL); + + sdi = malloc(sizeof(struct bhnd_usb_devinfo), M_DEVBUF, M_NOWAIT|M_ZERO); + if (sdi == NULL) + return (NULL); + + if (strncmp(name, "ohci", 4) == 0) + { + sdi->sdi_maddr = sc->sc_maddr + 0x000; + sdi->sdi_msize = 0x200; + sdi->sdi_irq = sc->sc_irqn; + BHND_INFO_DEV(dev, "ohci: irq=%d maddr=0x%jx", sdi->sdi_irq, + sdi->sdi_maddr); + } + else if (strncmp(name, "ehci", 4) == 0) + { + sdi->sdi_maddr = sc->sc_maddr + 0x000; + sdi->sdi_msize = 0x1000; + sdi->sdi_irq = sc->sc_irqn; + BHND_INFO_DEV(dev, "ehci: irq=%d maddr=0x%jx", sdi->sdi_irq, + sdi->sdi_maddr); + } + else + { + panic("Unknown subdevice"); + /* Unknown subdevice */ + sdi->sdi_maddr = 1; + sdi->sdi_msize = 1; + sdi->sdi_irq = 1; + } + + resource_list_init(&sdi->sdi_rl); + + /* + * Determine memory window on bus and irq if one is needed. + */ + resource_list_add(&sdi->sdi_rl, SYS_RES_MEMORY, 0, + sdi->sdi_maddr, sdi->sdi_maddr + sdi->sdi_msize - 1, sdi->sdi_msize); + + resource_list_add(&sdi->sdi_rl, SYS_RES_IRQ, 0, + sdi->sdi_irq, sdi->sdi_irq, 1); + + device_set_ivars(child, sdi); + return (child); + +} + +static device_method_t bhnd_usb_methods[] = { + /* Device interface */ + DEVMETHOD(device_attach, bhnd_usb_attach), + DEVMETHOD(device_probe, bhnd_usb_probe), + + /* Bus interface */ + DEVMETHOD(bus_add_child, bhnd_usb_add_child), + DEVMETHOD(bus_alloc_resource, bhnd_usb_alloc_resource), + DEVMETHOD(bus_get_resource_list, bhnd_usb_get_reslist), + DEVMETHOD(bus_print_child, bhnd_usb_print_child), + DEVMETHOD(bus_release_resource, bhnd_usb_release_resource), + /* Bus interface: generic part */ + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + DEVMETHOD_END +}; + +static devclass_t bhnd_usb_devclass; + +DEFINE_CLASS_0(bhnd_usb, bhnd_usb_driver, bhnd_usb_methods, + sizeof(struct bhnd_usb_softc)); +DRIVER_MODULE(bhnd_usb, bhnd, bhnd_usb_driver, bhnd_usb_devclass, 0, 0); + +MODULE_VERSION(bhnd_usb, 1); Property changes on: projects/clang390-import/sys/dev/bhnd/cores/usb/bhnd_usb.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang390-import/sys/dev/bhnd/cores/usb/bhnd_usbvar.h =================================================================== --- projects/clang390-import/sys/dev/bhnd/cores/usb/bhnd_usbvar.h (nonexistent) +++ projects/clang390-import/sys/dev/bhnd/cores/usb/bhnd_usbvar.h (revision 305431) @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 2010, Aleksandr Rybalko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#ifndef _BHND_USBVAR_H_ +#define _BHND_USBVAR_H_ + +struct bhnd_usb_softc { + bus_space_tag_t sc_bt; + bus_space_handle_t sc_bh; + bus_addr_t sc_maddr; + bus_size_t sc_msize; + bus_addr_t sc_irqn; + struct intr_event *sc_events; /* IRQ events structs */ + + struct resource *sc_mem; + struct resource *sc_irq; + struct rman mem_rman; + struct rman irq_rman; + int devid; + +}; + +struct bhnd_usb_devinfo { + struct resource_list sdi_rl; + uint8_t sdi_unit; /* core index on bus */ + uint8_t sdi_irq; + char sdi_name[8]; + rman_res_t sdi_maddr; + rman_res_t sdi_msize; +}; + +#endif /* _BHND_USBVAR_H_ */ Property changes on: projects/clang390-import/sys/dev/bhnd/cores/usb/bhnd_usbvar.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang390-import/sys/dev/hyperv/netvsc/hv_net_vsc.c =================================================================== --- projects/clang390-import/sys/dev/hyperv/netvsc/hv_net_vsc.c (revision 305430) +++ projects/clang390-import/sys/dev/hyperv/netvsc/hv_net_vsc.c (revision 305431) @@ -1,892 +1,918 @@ /*- * Copyright (c) 2009-2012,2016 Microsoft Corp. * Copyright (c) 2010-2012 Citrix Inc. * Copyright (c) 2012 NetApp Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /** * HyperV vmbus network VSC (virtual services client) module * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_NETVSC, "netvsc", "Hyper-V netvsc driver"); /* * Forward declarations */ static void hv_nv_on_channel_callback(struct vmbus_channel *chan, void *xrxr); static int hv_nv_init_send_buffer_with_net_vsp(struct hn_softc *sc); static int hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *, int); static int hv_nv_destroy_send_buffer(struct hn_softc *sc); static int hv_nv_destroy_rx_buffer(struct hn_softc *sc); static int hv_nv_connect_to_vsp(struct hn_softc *sc); static void hv_nv_on_send_completion(struct hn_softc *sc, struct vmbus_channel *, const struct vmbus_chanpkt_hdr *pkt); static void hv_nv_on_receive_completion(struct vmbus_channel *chan, uint64_t tid); static void hv_nv_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr, struct vmbus_channel *chan, const struct vmbus_chanpkt_hdr *pkt); static void hn_nvs_sent_none(struct hn_send_ctx *sndc, struct hn_softc *, struct vmbus_channel *chan, const void *, int); struct hn_send_ctx hn_send_ctx_none = HN_SEND_CTX_INITIALIZER(hn_nvs_sent_none, NULL); uint32_t hn_chim_alloc(struct hn_softc *sc) { int i, bmap_cnt = sc->hn_chim_bmap_cnt; u_long *bmap = sc->hn_chim_bmap; uint32_t ret = HN_NVS_CHIM_IDX_INVALID; for (i = 0; i < bmap_cnt; ++i) { int idx; idx = ffsl(~bmap[i]); if (idx == 0) continue; --idx; /* ffsl is 1-based */ KASSERT(i * LONG_BIT + idx < sc->hn_chim_cnt, ("invalid i %d and idx %d", i, idx)); if (atomic_testandset_long(&bmap[i], idx)) continue; ret = i * LONG_BIT + idx; break; } return (ret); } const void * hn_nvs_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, void *req, int reqlen, size_t *resplen0, uint32_t type) { struct hn_send_ctx sndc; size_t resplen, min_resplen = *resplen0; const struct hn_nvs_hdr *hdr; int error; KASSERT(min_resplen >= sizeof(*hdr), ("invalid minimum response len %zu", min_resplen)); /* * Execute the xact setup by the caller. */ hn_send_ctx_init_simple(&sndc, hn_nvs_sent_xact, xact); vmbus_xact_activate(xact); error = hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_RC, req, reqlen, &sndc); if (error) { vmbus_xact_deactivate(xact); return (NULL); } hdr = vmbus_xact_wait(xact, &resplen); /* * Check this NVS response message. */ if (resplen < min_resplen) { if_printf(sc->hn_ifp, "invalid NVS resp len %zu\n", resplen); return (NULL); } if (hdr->nvs_type != type) { if_printf(sc->hn_ifp, "unexpected NVS resp 0x%08x, " "expect 0x%08x\n", hdr->nvs_type, type); return (NULL); } /* All pass! */ *resplen0 = resplen; return (hdr); } static __inline int hn_nvs_req_send(struct hn_softc *sc, void *req, int reqlen) { return (hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_NONE, req, reqlen, &hn_send_ctx_none)); } /* * Net VSC initialize receive buffer with net VSP * * Net VSP: Network virtual services client, also known as the * Hyper-V extensible switch and the synthetic data path. */ static int hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *sc, int rxbuf_size) { struct vmbus_xact *xact = NULL; struct hn_nvs_rxbuf_conn *conn; const struct hn_nvs_rxbuf_connresp *resp; size_t resp_len; uint32_t status; int error; KASSERT(rxbuf_size <= NETVSC_RECEIVE_BUFFER_SIZE, ("invalid rxbuf size %d", rxbuf_size)); /* * Connect the RXBUF GPADL to the primary channel. * * NOTE: * Only primary channel has RXBUF connected to it. Sub-channels * just share this RXBUF. */ error = vmbus_chan_gpadl_connect(sc->hn_prichan, sc->hn_rxbuf_dma.hv_paddr, rxbuf_size, &sc->hn_rxbuf_gpadl); if (error) { if_printf(sc->hn_ifp, "rxbuf gpadl connect failed: %d\n", error); goto cleanup; } /* * Connect RXBUF to NVS. */ xact = vmbus_xact_get(sc->hn_xact, sizeof(*conn)); if (xact == NULL) { if_printf(sc->hn_ifp, "no xact for nvs rxbuf conn\n"); error = ENXIO; goto cleanup; } conn = vmbus_xact_req_data(xact); conn->nvs_type = HN_NVS_TYPE_RXBUF_CONN; conn->nvs_gpadl = sc->hn_rxbuf_gpadl; conn->nvs_sig = HN_NVS_RXBUF_SIG; resp_len = sizeof(*resp); resp = hn_nvs_xact_execute(sc, xact, conn, sizeof(*conn), &resp_len, HN_NVS_TYPE_RXBUF_CONNRESP); if (resp == NULL) { if_printf(sc->hn_ifp, "exec rxbuf conn failed\n"); error = EIO; goto cleanup; } status = resp->nvs_status; vmbus_xact_put(xact); xact = NULL; if (status != HN_NVS_STATUS_OK) { if_printf(sc->hn_ifp, "rxbuf conn failed: %x\n", status); error = EIO; goto cleanup; } sc->hn_flags |= HN_FLAG_RXBUF_CONNECTED; return (0); cleanup: if (xact != NULL) vmbus_xact_put(xact); hv_nv_destroy_rx_buffer(sc); return (error); } /* * Net VSC initialize send buffer with net VSP */ static int hv_nv_init_send_buffer_with_net_vsp(struct hn_softc *sc) { struct vmbus_xact *xact = NULL; struct hn_nvs_chim_conn *chim; const struct hn_nvs_chim_connresp *resp; size_t resp_len; uint32_t status, sectsz; int error; /* * Connect chimney sending buffer GPADL to the primary channel. * * NOTE: * Only primary channel has chimney sending buffer connected to it. * Sub-channels just share this chimney sending buffer. */ error = vmbus_chan_gpadl_connect(sc->hn_prichan, sc->hn_chim_dma.hv_paddr, NETVSC_SEND_BUFFER_SIZE, &sc->hn_chim_gpadl); if (error) { if_printf(sc->hn_ifp, "chimney sending buffer gpadl " "connect failed: %d\n", error); goto cleanup; } /* * Connect chimney sending buffer to NVS */ xact = vmbus_xact_get(sc->hn_xact, sizeof(*chim)); if (xact == NULL) { if_printf(sc->hn_ifp, "no xact for nvs chim conn\n"); error = ENXIO; goto cleanup; } chim = vmbus_xact_req_data(xact); chim->nvs_type = HN_NVS_TYPE_CHIM_CONN; chim->nvs_gpadl = sc->hn_chim_gpadl; chim->nvs_sig = HN_NVS_CHIM_SIG; resp_len = sizeof(*resp); resp = hn_nvs_xact_execute(sc, xact, chim, sizeof(*chim), &resp_len, HN_NVS_TYPE_CHIM_CONNRESP); if (resp == NULL) { if_printf(sc->hn_ifp, "exec chim conn failed\n"); error = EIO; goto cleanup; } status = resp->nvs_status; sectsz = resp->nvs_sectsz; vmbus_xact_put(xact); xact = NULL; if (status != HN_NVS_STATUS_OK) { if_printf(sc->hn_ifp, "chim conn failed: %x\n", status); error = EIO; goto cleanup; } if (sectsz == 0) { if_printf(sc->hn_ifp, "zero chimney sending buffer " "section size\n"); return 0; } sc->hn_chim_szmax = sectsz; sc->hn_chim_cnt = NETVSC_SEND_BUFFER_SIZE / sc->hn_chim_szmax; if (NETVSC_SEND_BUFFER_SIZE % sc->hn_chim_szmax != 0) { if_printf(sc->hn_ifp, "chimney sending sections are " "not properly aligned\n"); } if (sc->hn_chim_cnt % LONG_BIT != 0) { if_printf(sc->hn_ifp, "discard %d chimney sending sections\n", sc->hn_chim_cnt % LONG_BIT); } sc->hn_chim_bmap_cnt = sc->hn_chim_cnt / LONG_BIT; sc->hn_chim_bmap = malloc(sc->hn_chim_bmap_cnt * sizeof(u_long), M_NETVSC, M_WAITOK | M_ZERO); /* Done! */ sc->hn_flags |= HN_FLAG_CHIM_CONNECTED; if (bootverbose) { if_printf(sc->hn_ifp, "chimney sending buffer %d/%d\n", sc->hn_chim_szmax, sc->hn_chim_cnt); } return 0; cleanup: if (xact != NULL) vmbus_xact_put(xact); hv_nv_destroy_send_buffer(sc); return (error); } /* * Net VSC destroy receive buffer */ static int hv_nv_destroy_rx_buffer(struct hn_softc *sc) { int ret = 0; if (sc->hn_flags & HN_FLAG_RXBUF_CONNECTED) { struct hn_nvs_rxbuf_disconn disconn; /* * Disconnect RXBUF from NVS. */ memset(&disconn, 0, sizeof(disconn)); disconn.nvs_type = HN_NVS_TYPE_RXBUF_DISCONN; disconn.nvs_sig = HN_NVS_RXBUF_SIG; /* NOTE: No response. */ ret = hn_nvs_req_send(sc, &disconn, sizeof(disconn)); if (ret != 0) { if_printf(sc->hn_ifp, "send rxbuf disconn failed: %d\n", ret); return (ret); } sc->hn_flags &= ~HN_FLAG_RXBUF_CONNECTED; } if (sc->hn_rxbuf_gpadl != 0) { /* * Disconnect RXBUF from primary channel. */ ret = vmbus_chan_gpadl_disconnect(sc->hn_prichan, sc->hn_rxbuf_gpadl); if (ret != 0) { if_printf(sc->hn_ifp, "rxbuf disconn failed: %d\n", ret); return (ret); } sc->hn_rxbuf_gpadl = 0; } return (ret); } /* * Net VSC destroy send buffer */ static int hv_nv_destroy_send_buffer(struct hn_softc *sc) { int ret = 0; if (sc->hn_flags & HN_FLAG_CHIM_CONNECTED) { struct hn_nvs_chim_disconn disconn; /* * Disconnect chimney sending buffer from NVS. */ memset(&disconn, 0, sizeof(disconn)); disconn.nvs_type = HN_NVS_TYPE_CHIM_DISCONN; disconn.nvs_sig = HN_NVS_CHIM_SIG; /* NOTE: No response. */ ret = hn_nvs_req_send(sc, &disconn, sizeof(disconn)); if (ret != 0) { if_printf(sc->hn_ifp, "send chim disconn failed: %d\n", ret); return (ret); } sc->hn_flags &= ~HN_FLAG_CHIM_CONNECTED; } if (sc->hn_chim_gpadl != 0) { /* * Disconnect chimney sending buffer from primary channel. */ ret = vmbus_chan_gpadl_disconnect(sc->hn_prichan, sc->hn_chim_gpadl); if (ret != 0) { if_printf(sc->hn_ifp, "chim disconn failed: %d\n", ret); return (ret); } sc->hn_chim_gpadl = 0; } if (sc->hn_chim_bmap != NULL) { free(sc->hn_chim_bmap, M_NETVSC); sc->hn_chim_bmap = NULL; } return (ret); } static int hv_nv_negotiate_nvsp_protocol(struct hn_softc *sc, uint32_t nvs_ver) { struct vmbus_xact *xact; struct hn_nvs_init *init; const struct hn_nvs_init_resp *resp; size_t resp_len; uint32_t status; xact = vmbus_xact_get(sc->hn_xact, sizeof(*init)); if (xact == NULL) { if_printf(sc->hn_ifp, "no xact for nvs init\n"); return (ENXIO); } init = vmbus_xact_req_data(xact); init->nvs_type = HN_NVS_TYPE_INIT; init->nvs_ver_min = nvs_ver; init->nvs_ver_max = nvs_ver; resp_len = sizeof(*resp); resp = hn_nvs_xact_execute(sc, xact, init, sizeof(*init), &resp_len, HN_NVS_TYPE_INIT_RESP); if (resp == NULL) { if_printf(sc->hn_ifp, "exec init failed\n"); vmbus_xact_put(xact); return (EIO); } status = resp->nvs_status; vmbus_xact_put(xact); if (status != HN_NVS_STATUS_OK) { if_printf(sc->hn_ifp, "nvs init failed for ver 0x%x\n", nvs_ver); return (EINVAL); } return (0); } /* * Send NDIS version 2 config packet containing MTU. * * Not valid for NDIS version 1. */ static int hv_nv_send_ndis_config(struct hn_softc *sc, uint32_t mtu) { struct hn_nvs_ndis_conf conf; int error; memset(&conf, 0, sizeof(conf)); conf.nvs_type = HN_NVS_TYPE_NDIS_CONF; conf.nvs_mtu = mtu; conf.nvs_caps = HN_NVS_NDIS_CONF_VLAN; /* NOTE: No response. */ error = hn_nvs_req_send(sc, &conf, sizeof(conf)); if (error) if_printf(sc->hn_ifp, "send nvs ndis conf failed: %d\n", error); return (error); } /* * Net VSC connect to VSP */ static int hv_nv_connect_to_vsp(struct hn_softc *sc) { uint32_t protocol_list[] = { NVSP_PROTOCOL_VERSION_1, NVSP_PROTOCOL_VERSION_2, NVSP_PROTOCOL_VERSION_4, NVSP_PROTOCOL_VERSION_5 }; int i; int protocol_number = nitems(protocol_list); int ret = 0; device_t dev = sc->hn_dev; struct ifnet *ifp = sc->hn_ifp; struct hn_nvs_ndis_init ndis; int rxbuf_size; /* * Negotiate the NVSP version. Try the latest NVSP first. */ for (i = protocol_number - 1; i >= 0; i--) { if (hv_nv_negotiate_nvsp_protocol(sc, protocol_list[i]) == 0) { sc->hn_nvs_ver = protocol_list[i]; sc->hn_ndis_ver = NDIS_VERSION_6_30; if (sc->hn_nvs_ver <= NVSP_PROTOCOL_VERSION_4) sc->hn_ndis_ver = NDIS_VERSION_6_1; if (bootverbose) { if_printf(sc->hn_ifp, "NVS version 0x%x, " "NDIS version %u.%u\n", sc->hn_nvs_ver, NDIS_VERSION_MAJOR(sc->hn_ndis_ver), NDIS_VERSION_MINOR(sc->hn_ndis_ver)); } break; } } if (i < 0) { if (bootverbose) device_printf(dev, "failed to negotiate a valid " "protocol.\n"); return (EPROTO); } /* * Set the MTU if supported by this NVSP protocol version * This needs to be right after the NVSP init message per Haiyang */ if (sc->hn_nvs_ver >= NVSP_PROTOCOL_VERSION_2) ret = hv_nv_send_ndis_config(sc, ifp->if_mtu); /* * Initialize NDIS. */ memset(&ndis, 0, sizeof(ndis)); ndis.nvs_type = HN_NVS_TYPE_NDIS_INIT; ndis.nvs_ndis_major = NDIS_VERSION_MAJOR(sc->hn_ndis_ver); ndis.nvs_ndis_minor = NDIS_VERSION_MINOR(sc->hn_ndis_ver); /* NOTE: No response. */ ret = hn_nvs_req_send(sc, &ndis, sizeof(ndis)); if (ret != 0) { if_printf(sc->hn_ifp, "send nvs ndis init failed: %d\n", ret); goto cleanup; } /* Post the big receive buffer to NetVSP */ if (sc->hn_nvs_ver <= NVSP_PROTOCOL_VERSION_2) rxbuf_size = NETVSC_RECEIVE_BUFFER_SIZE_LEGACY; else rxbuf_size = NETVSC_RECEIVE_BUFFER_SIZE; ret = hv_nv_init_rx_buffer_with_net_vsp(sc, rxbuf_size); if (ret == 0) ret = hv_nv_init_send_buffer_with_net_vsp(sc); cleanup: return (ret); } /* * Net VSC disconnect from VSP */ static void hv_nv_disconnect_from_vsp(struct hn_softc *sc) { hv_nv_destroy_rx_buffer(sc); hv_nv_destroy_send_buffer(sc); } void hv_nv_subchan_attach(struct vmbus_channel *chan, struct hn_rx_ring *rxr) { KASSERT(rxr->hn_rx_idx == vmbus_chan_subidx(chan), ("chan%u subidx %u, rxr%d mismatch", vmbus_chan_id(chan), vmbus_chan_subidx(chan), rxr->hn_rx_idx)); vmbus_chan_open(chan, NETVSC_DEVICE_RING_BUFFER_SIZE, NETVSC_DEVICE_RING_BUFFER_SIZE, NULL, 0, hv_nv_on_channel_callback, rxr); } /* * Net VSC on device add * * Callback when the device belonging to this driver is added */ int hv_nv_on_device_add(struct hn_softc *sc, struct hn_rx_ring *rxr) { struct vmbus_channel *chan = sc->hn_prichan; int ret = 0; /* * Open the channel */ KASSERT(rxr->hn_rx_idx == vmbus_chan_subidx(chan), ("chan%u subidx %u, rxr%d mismatch", vmbus_chan_id(chan), vmbus_chan_subidx(chan), rxr->hn_rx_idx)); ret = vmbus_chan_open(chan, NETVSC_DEVICE_RING_BUFFER_SIZE, NETVSC_DEVICE_RING_BUFFER_SIZE, NULL, 0, hv_nv_on_channel_callback, rxr); if (ret != 0) goto cleanup; /* * Connect with the NetVsp */ ret = hv_nv_connect_to_vsp(sc); if (ret != 0) goto close; return (0); close: /* Now, we can close the channel safely */ vmbus_chan_close(chan); cleanup: return (ret); } /* * Net VSC on device remove */ int hv_nv_on_device_remove(struct hn_softc *sc) { hv_nv_disconnect_from_vsp(sc); /* Now, we can close the channel safely */ vmbus_chan_close(sc->hn_prichan); return (0); } void hn_nvs_sent_xact(struct hn_send_ctx *sndc, struct hn_softc *sc __unused, struct vmbus_channel *chan __unused, const void *data, int dlen) { vmbus_xact_wakeup(sndc->hn_cbarg, data, dlen); } static void hn_nvs_sent_none(struct hn_send_ctx *sndc __unused, struct hn_softc *sc __unused, struct vmbus_channel *chan __unused, const void *data __unused, int dlen __unused) { /* EMPTY */ } void hn_chim_free(struct hn_softc *sc, uint32_t chim_idx) { u_long mask; uint32_t idx; idx = chim_idx / LONG_BIT; KASSERT(idx < sc->hn_chim_bmap_cnt, ("invalid chimney index 0x%x", chim_idx)); mask = 1UL << (chim_idx % LONG_BIT); KASSERT(sc->hn_chim_bmap[idx] & mask, ("index bitmap 0x%lx, chimney index %u, " "bitmap idx %d, bitmask 0x%lx", sc->hn_chim_bmap[idx], chim_idx, idx, mask)); atomic_clear_long(&sc->hn_chim_bmap[idx], mask); } /* * Net VSC on send completion */ static void hv_nv_on_send_completion(struct hn_softc *sc, struct vmbus_channel *chan, const struct vmbus_chanpkt_hdr *pkt) { struct hn_send_ctx *sndc; sndc = (struct hn_send_ctx *)(uintptr_t)pkt->cph_xactid; sndc->hn_cb(sndc, sc, chan, VMBUS_CHANPKT_CONST_DATA(pkt), VMBUS_CHANPKT_DATALEN(pkt)); /* * NOTE: * 'sndc' CAN NOT be accessed anymore, since it can be freed by * its callback. */ } /* * Net VSC on send * Sends a packet on the specified Hyper-V device. * Returns 0 on success, non-zero on failure. */ int hv_nv_on_send(struct vmbus_channel *chan, uint32_t rndis_mtype, struct hn_send_ctx *sndc, struct vmbus_gpa *gpa, int gpa_cnt) { struct hn_nvs_rndis rndis; int ret; rndis.nvs_type = HN_NVS_TYPE_RNDIS; rndis.nvs_rndis_mtype = rndis_mtype; rndis.nvs_chim_idx = sndc->hn_chim_idx; rndis.nvs_chim_sz = sndc->hn_chim_sz; if (gpa_cnt) { ret = hn_nvs_send_sglist(chan, gpa, gpa_cnt, &rndis, sizeof(rndis), sndc); } else { ret = hn_nvs_send(chan, VMBUS_CHANPKT_FLAG_RC, &rndis, sizeof(rndis), sndc); } return (ret); } /* * Net VSC on receive * * In the FreeBSD Hyper-V virtual world, this function deals exclusively * with virtual addresses. */ static void hv_nv_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr, struct vmbus_channel *chan, const struct vmbus_chanpkt_hdr *pkthdr) { const struct vmbus_chanpkt_rxbuf *pkt; const struct hn_nvs_hdr *nvs_hdr; - int count = 0; - int i = 0; + int count, i, hlen; - /* Make sure that this is a RNDIS message. */ + if (__predict_false(VMBUS_CHANPKT_DATALEN(pkthdr) < sizeof(*nvs_hdr))) { + if_printf(rxr->hn_ifp, "invalid nvs RNDIS\n"); + return; + } nvs_hdr = VMBUS_CHANPKT_CONST_DATA(pkthdr); + + /* Make sure that this is a RNDIS message. */ if (__predict_false(nvs_hdr->nvs_type != HN_NVS_TYPE_RNDIS)) { if_printf(rxr->hn_ifp, "nvs type %u, not RNDIS\n", nvs_hdr->nvs_type); return; } - + + hlen = VMBUS_CHANPKT_GETLEN(pkthdr->cph_hlen); + if (__predict_false(hlen < sizeof(*pkt))) { + if_printf(rxr->hn_ifp, "invalid rxbuf chanpkt\n"); + return; + } pkt = (const struct vmbus_chanpkt_rxbuf *)pkthdr; - if (pkt->cp_rxbuf_id != NETVSC_RECEIVE_BUFFER_ID) { - if_printf(rxr->hn_ifp, "rxbuf_id %d is invalid!\n", + if (__predict_false(pkt->cp_rxbuf_id != NETVSC_RECEIVE_BUFFER_ID)) { + if_printf(rxr->hn_ifp, "invalid rxbuf_id 0x%08x\n", pkt->cp_rxbuf_id); return; } count = pkt->cp_rxbuf_cnt; + if (__predict_false(hlen < + __offsetof(struct vmbus_chanpkt_rxbuf, cp_rxbuf[count]))) { + if_printf(rxr->hn_ifp, "invalid rxbuf_cnt %d\n", count); + return; + } /* Each range represents 1 RNDIS pkt that contains 1 Ethernet frame */ - for (i = 0; i < count; i++) { - hv_rf_on_receive(sc, rxr, - rxr->hn_rxbuf + pkt->cp_rxbuf[i].rb_ofs, - pkt->cp_rxbuf[i].rb_len); + for (i = 0; i < count; ++i) { + int ofs, len; + + ofs = pkt->cp_rxbuf[i].rb_ofs; + len = pkt->cp_rxbuf[i].rb_len; + if (__predict_false(ofs + len > NETVSC_RECEIVE_BUFFER_SIZE)) { + if_printf(rxr->hn_ifp, "%dth RNDIS msg overflow rxbuf, " + "ofs %d, len %d\n", i, ofs, len); + continue; + } + hv_rf_on_receive(sc, rxr, rxr->hn_rxbuf + ofs, len); } /* * Moved completion call back here so that all received * messages (not just data messages) will trigger a response * message back to the host. */ hv_nv_on_receive_completion(chan, pkt->cp_hdr.cph_xactid); } /* * Net VSC on receive completion * * Send a receive completion packet to RNDIS device (ie NetVsp) */ static void hv_nv_on_receive_completion(struct vmbus_channel *chan, uint64_t tid) { struct hn_nvs_rndis_ack ack; int retries = 0; int ret = 0; ack.nvs_type = HN_NVS_TYPE_RNDIS_ACK; ack.nvs_status = HN_NVS_STATUS_OK; retry_send_cmplt: /* Send the completion */ ret = vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_COMP, VMBUS_CHANPKT_FLAG_NONE, &ack, sizeof(ack), tid); if (ret == 0) { /* success */ /* no-op */ } else if (ret == EAGAIN) { /* no more room... wait a bit and attempt to retry 3 times */ retries++; if (retries < 4) { DELAY(100); goto retry_send_cmplt; } } } static void hn_proc_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt) { const struct hn_nvs_hdr *hdr; + if (VMBUS_CHANPKT_DATALEN(pkt) < sizeof(*hdr)) { + if_printf(sc->hn_ifp, "invalid nvs notify\n"); + return; + } hdr = VMBUS_CHANPKT_CONST_DATA(pkt); + if (hdr->nvs_type == HN_NVS_TYPE_TXTBL_NOTE) { /* Useless; ignore */ return; } if_printf(sc->hn_ifp, "got notify, nvs type %u\n", hdr->nvs_type); } /* * Net VSC on channel callback */ static void hv_nv_on_channel_callback(struct vmbus_channel *chan, void *xrxr) { struct hn_rx_ring *rxr = xrxr; struct hn_softc *sc = rxr->hn_ifp->if_softc; void *buffer; int bufferlen = NETVSC_PACKET_SIZE; buffer = rxr->hn_rdbuf; do { struct vmbus_chanpkt_hdr *pkt = buffer; uint32_t bytes_rxed; int ret; bytes_rxed = bufferlen; ret = vmbus_chan_recv_pkt(chan, pkt, &bytes_rxed); if (ret == 0) { if (bytes_rxed > 0) { switch (pkt->cph_type) { case VMBUS_CHANPKT_TYPE_COMP: hv_nv_on_send_completion(sc, chan, pkt); break; case VMBUS_CHANPKT_TYPE_RXBUF: hv_nv_on_receive(sc, rxr, chan, pkt); break; case VMBUS_CHANPKT_TYPE_INBAND: hn_proc_notify(sc, pkt); break; default: if_printf(rxr->hn_ifp, "unknown chan pkt %u\n", pkt->cph_type); break; } } } else if (ret == ENOBUFS) { /* Handle large packet */ if (bufferlen > NETVSC_PACKET_SIZE) { free(buffer, M_NETVSC); buffer = NULL; } /* alloc new buffer */ buffer = malloc(bytes_rxed, M_NETVSC, M_NOWAIT); if (buffer == NULL) { if_printf(rxr->hn_ifp, "hv_cb malloc buffer failed, len=%u\n", bytes_rxed); bufferlen = 0; break; } bufferlen = bytes_rxed; } else { /* No more packets */ break; } } while (1); if (bufferlen > NETVSC_PACKET_SIZE) free(buffer, M_NETVSC); hv_rf_channel_rollup(rxr, rxr->hn_txr); } Index: projects/clang390-import/sys/dev/hyperv/netvsc/hv_rndis_filter.c =================================================================== --- projects/clang390-import/sys/dev/hyperv/netvsc/hv_rndis_filter.c (revision 305430) +++ projects/clang390-import/sys/dev/hyperv/netvsc/hv_rndis_filter.c (revision 305431) @@ -1,1045 +1,1059 @@ /*- * Copyright (c) 2009-2012,2016 Microsoft Corp. * Copyright (c) 2010-2012 Citrix Inc. * Copyright (c) 2012 NetApp Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define HV_RF_RECVINFO_VLAN 0x1 #define HV_RF_RECVINFO_CSUM 0x2 #define HV_RF_RECVINFO_HASHINF 0x4 #define HV_RF_RECVINFO_HASHVAL 0x8 #define HV_RF_RECVINFO_ALL \ (HV_RF_RECVINFO_VLAN | \ HV_RF_RECVINFO_CSUM | \ HV_RF_RECVINFO_HASHINF | \ HV_RF_RECVINFO_HASHVAL) #define HN_RNDIS_RID_COMPAT_MASK 0xffff #define HN_RNDIS_RID_COMPAT_MAX HN_RNDIS_RID_COMPAT_MASK #define HN_RNDIS_XFER_SIZE 2048 /* * Forward declarations */ static void hv_rf_receive_indicate_status(struct hn_softc *sc, - const rndis_msg *response); + const void *data, int dlen); static void hv_rf_receive_data(struct hn_rx_ring *rxr, const void *data, int dlen); static int hv_rf_query_device_mac(struct hn_softc *sc, uint8_t *eaddr); static int hv_rf_query_device_link_status(struct hn_softc *sc, uint32_t *link_status); static int hv_rf_init_device(struct hn_softc *sc); static int hn_rndis_query(struct hn_softc *sc, uint32_t oid, const void *idata, size_t idlen, void *odata, size_t *odlen0); static int hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, size_t dlen); static int hn_rndis_conf_offload(struct hn_softc *sc); static int hn_rndis_get_rsscaps(struct hn_softc *sc, int *rxr_cnt); static int hn_rndis_conf_rss(struct hn_softc *sc, int nchan); static __inline uint32_t hn_rndis_rid(struct hn_softc *sc) { uint32_t rid; again: rid = atomic_fetchadd_int(&sc->hn_rndis_rid, 1); if (rid == 0) goto again; /* Use upper 16 bits for non-compat RNDIS messages. */ return ((rid & 0xffff) << 16); } /* * Set the Per-Packet-Info with the specified type */ void * hv_set_rppi_data(rndis_msg *rndis_mesg, uint32_t rppi_size, int pkt_type) { rndis_packet *rndis_pkt; rndis_per_packet_info *rppi; rndis_pkt = &rndis_mesg->msg.packet; rndis_pkt->data_offset += rppi_size; rppi = (rndis_per_packet_info *)((char *)rndis_pkt + rndis_pkt->per_pkt_info_offset + rndis_pkt->per_pkt_info_length); rppi->size = rppi_size; rppi->type = pkt_type; rppi->per_packet_info_offset = sizeof(rndis_per_packet_info); rndis_pkt->per_pkt_info_length += rppi_size; return (rppi); } /* * RNDIS filter receive indicate status */ static void -hv_rf_receive_indicate_status(struct hn_softc *sc, const rndis_msg *response) +hv_rf_receive_indicate_status(struct hn_softc *sc, const void *data, int dlen) { - const rndis_indicate_status *indicate = &response->msg.indicate_status; - - switch(indicate->status) { + const struct rndis_status_msg *msg; + + if (dlen < sizeof(*msg)) { + if_printf(sc->hn_ifp, "invalid RNDIS status\n"); + return; + } + msg = data; + + switch (msg->rm_status) { case RNDIS_STATUS_MEDIA_CONNECT: netvsc_linkstatus_callback(sc, 1); break; + case RNDIS_STATUS_MEDIA_DISCONNECT: netvsc_linkstatus_callback(sc, 0); break; + default: /* TODO: */ - if_printf(sc->hn_ifp, - "unknown status %d received\n", indicate->status); + if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n", + msg->rm_status); break; } } static int hv_rf_find_recvinfo(const rndis_packet *rpkt, struct hn_recvinfo *info) { const struct rndis_pktinfo *pi; uint32_t mask = 0, len; info->vlan_info = HN_NDIS_VLAN_INFO_INVALID; info->csum_info = HN_NDIS_RXCSUM_INFO_INVALID; info->hash_info = HN_NDIS_HASH_INFO_INVALID; if (rpkt->per_pkt_info_offset == 0) return (0); if (__predict_false(rpkt->per_pkt_info_offset & (RNDIS_PKTINFO_ALIGN - 1))) return (EINVAL); if (__predict_false(rpkt->per_pkt_info_offset < RNDIS_PACKET_MSG_OFFSET_MIN)) return (EINVAL); pi = (const struct rndis_pktinfo *) ((const uint8_t *)rpkt + rpkt->per_pkt_info_offset); len = rpkt->per_pkt_info_length; while (len != 0) { const void *data; uint32_t dlen; if (__predict_false(len < sizeof(*pi))) return (EINVAL); if (__predict_false(len < pi->rm_size)) return (EINVAL); len -= pi->rm_size; if (__predict_false(pi->rm_size & (RNDIS_PKTINFO_ALIGN - 1))) return (EINVAL); if (__predict_false(pi->rm_size < pi->rm_pktinfooffset)) return (EINVAL); dlen = pi->rm_size - pi->rm_pktinfooffset; data = pi->rm_data; switch (pi->rm_type) { case NDIS_PKTINFO_TYPE_VLAN: if (__predict_false(dlen < NDIS_VLAN_INFO_SIZE)) return (EINVAL); info->vlan_info = *((const uint32_t *)data); mask |= HV_RF_RECVINFO_VLAN; break; case NDIS_PKTINFO_TYPE_CSUM: if (__predict_false(dlen < NDIS_RXCSUM_INFO_SIZE)) return (EINVAL); info->csum_info = *((const uint32_t *)data); mask |= HV_RF_RECVINFO_CSUM; break; case HN_NDIS_PKTINFO_TYPE_HASHVAL: if (__predict_false(dlen < HN_NDIS_HASH_VALUE_SIZE)) return (EINVAL); info->hash_value = *((const uint32_t *)data); mask |= HV_RF_RECVINFO_HASHVAL; break; case HN_NDIS_PKTINFO_TYPE_HASHINF: if (__predict_false(dlen < HN_NDIS_HASH_INFO_SIZE)) return (EINVAL); info->hash_info = *((const uint32_t *)data); mask |= HV_RF_RECVINFO_HASHINF; break; default: goto next; } if (mask == HV_RF_RECVINFO_ALL) { /* All found; done */ break; } next: pi = (const struct rndis_pktinfo *) ((const uint8_t *)pi + pi->rm_size); } /* * Final fixup. * - If there is no hash value, invalidate the hash info. */ if ((mask & HV_RF_RECVINFO_HASHVAL) == 0) info->hash_info = HN_NDIS_HASH_INFO_INVALID; return (0); } /* * RNDIS filter receive data */ static void hv_rf_receive_data(struct hn_rx_ring *rxr, const void *data, int dlen) { const rndis_msg *message = data; const rndis_packet *rndis_pkt; uint32_t data_offset; struct hn_recvinfo info; rndis_pkt = &message->msg.packet; /* * Fixme: Handle multiple rndis pkt msgs that may be enclosed in this * netvsc packet (ie tot_data_buf_len != message_length) */ /* Remove rndis header, then pass data packet up the stack */ data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset; dlen -= data_offset; if (dlen < rndis_pkt->data_length) { if_printf(rxr->hn_ifp, "total length %u is less than data length %u\n", dlen, rndis_pkt->data_length); return; } dlen = rndis_pkt->data_length; data = (const uint8_t *)data + data_offset; if (hv_rf_find_recvinfo(rndis_pkt, &info)) { if_printf(rxr->hn_ifp, "recvinfo parsing failed\n"); return; } netvsc_recv(rxr, data, dlen, &info); } /* * RNDIS filter on receive */ -int +void hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr, const void *data, int dlen) { - const rndis_msg *rndis_hdr; const struct rndis_comp_hdr *comp; + const struct rndis_msghdr *hdr; - rndis_hdr = data; - switch (rndis_hdr->ndis_msg_type) { - /* data message */ + if (__predict_false(dlen < sizeof(*hdr))) { + if_printf(rxr->hn_ifp, "invalid RNDIS msg\n"); + return; + } + hdr = data; + + switch (hdr->rm_type) { case REMOTE_NDIS_PACKET_MSG: hv_rf_receive_data(rxr, data, dlen); break; - /* completion messages */ case REMOTE_NDIS_INITIALIZE_CMPLT: case REMOTE_NDIS_QUERY_CMPLT: case REMOTE_NDIS_SET_CMPLT: - case REMOTE_NDIS_KEEPALIVE_CMPLT: + case REMOTE_NDIS_KEEPALIVE_CMPLT: /* unused */ + if (dlen < sizeof(*comp)) { + if_printf(rxr->hn_ifp, "invalid RNDIS cmplt\n"); + return; + } comp = data; + KASSERT(comp->rm_rid > HN_RNDIS_RID_COMPAT_MAX, - ("invalid rid 0x%08x\n", comp->rm_rid)); + ("invalid RNDIS rid 0x%08x\n", comp->rm_rid)); vmbus_xact_ctx_wakeup(sc->hn_xact, comp, dlen); break; - /* notification message */ case REMOTE_NDIS_INDICATE_STATUS_MSG: - hv_rf_receive_indicate_status(sc, rndis_hdr); + hv_rf_receive_indicate_status(sc, data, dlen); break; case REMOTE_NDIS_RESET_CMPLT: /* * Reset completed, no rid. * * NOTE: * RESET is not issued by hn(4), so this message should * _not_ be observed. */ - if_printf(sc->hn_ifp, "RESET CMPLT received\n"); + if_printf(rxr->hn_ifp, "RESET cmplt received\n"); break; default: - if_printf(sc->hn_ifp, "unknown RNDIS message 0x%x\n", - rndis_hdr->ndis_msg_type); + if_printf(rxr->hn_ifp, "unknown RNDIS msg 0x%x\n", + hdr->rm_type); break; } - return (0); } /* * RNDIS filter query device MAC address */ static int hv_rf_query_device_mac(struct hn_softc *sc, uint8_t *eaddr) { size_t eaddr_len; int error; eaddr_len = ETHER_ADDR_LEN; error = hn_rndis_query(sc, OID_802_3_PERMANENT_ADDRESS, NULL, 0, eaddr, &eaddr_len); if (error) return (error); if (eaddr_len != ETHER_ADDR_LEN) { if_printf(sc->hn_ifp, "invalid eaddr len %zu\n", eaddr_len); return (EINVAL); } return (0); } /* * RNDIS filter query device link status */ static int hv_rf_query_device_link_status(struct hn_softc *sc, uint32_t *link_status) { size_t size; int error; size = sizeof(*link_status); error = hn_rndis_query(sc, OID_GEN_MEDIA_CONNECT_STATUS, NULL, 0, link_status, &size); if (error) return (error); if (size != sizeof(uint32_t)) { if_printf(sc->hn_ifp, "invalid link status len %zu\n", size); return (EINVAL); } return (0); } static uint8_t netvsc_hash_key[NDIS_HASH_KEYSIZE_TOEPLITZ] = { 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2, 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0, 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4, 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c, 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa }; static const void * hn_rndis_xact_exec1(struct hn_softc *sc, struct vmbus_xact *xact, size_t reqlen, struct hn_send_ctx *sndc, size_t *comp_len) { struct vmbus_gpa gpa[HN_XACT_REQ_PGCNT]; int gpa_cnt, error; bus_addr_t paddr; KASSERT(reqlen <= HN_XACT_REQ_SIZE && reqlen > 0, ("invalid request length %zu", reqlen)); /* * Setup the SG list. */ paddr = vmbus_xact_req_paddr(xact); KASSERT((paddr & PAGE_MASK) == 0, ("vmbus xact request is not page aligned 0x%jx", (uintmax_t)paddr)); for (gpa_cnt = 0; gpa_cnt < HN_XACT_REQ_PGCNT; ++gpa_cnt) { int len = PAGE_SIZE; if (reqlen == 0) break; if (reqlen < len) len = reqlen; gpa[gpa_cnt].gpa_page = atop(paddr) + gpa_cnt; gpa[gpa_cnt].gpa_len = len; gpa[gpa_cnt].gpa_ofs = 0; reqlen -= len; } KASSERT(reqlen == 0, ("still have %zu request data left", reqlen)); /* * Send this RNDIS control message and wait for its completion * message. */ vmbus_xact_activate(xact); error = hv_nv_on_send(sc->hn_prichan, HN_NVS_RNDIS_MTYPE_CTRL, sndc, gpa, gpa_cnt); if (error) { vmbus_xact_deactivate(xact); if_printf(sc->hn_ifp, "RNDIS ctrl send failed: %d\n", error); return (NULL); } return (vmbus_xact_wait(xact, comp_len)); } static const void * hn_rndis_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, uint32_t rid, size_t reqlen, size_t *comp_len0, uint32_t comp_type) { const struct rndis_comp_hdr *comp; size_t comp_len, min_complen = *comp_len0; KASSERT(rid > HN_RNDIS_RID_COMPAT_MAX, ("invalid rid %u\n", rid)); KASSERT(min_complen >= sizeof(*comp), ("invalid minimum complete len %zu", min_complen)); /* * Execute the xact setup by the caller. */ comp = hn_rndis_xact_exec1(sc, xact, reqlen, &hn_send_ctx_none, &comp_len); if (comp == NULL) return (NULL); /* * Check this RNDIS complete message. */ if (comp_len < min_complen) { if (comp_len >= sizeof(*comp)) { /* rm_status field is valid */ if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu, " "status 0x%08x\n", comp_len, comp->rm_status); } else { if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n", comp_len); } return (NULL); } if (comp->rm_len < min_complen) { if_printf(sc->hn_ifp, "invalid RNDIS comp msglen %u\n", comp->rm_len); return (NULL); } if (comp->rm_type != comp_type) { if_printf(sc->hn_ifp, "unexpected RNDIS comp 0x%08x, " "expect 0x%08x\n", comp->rm_type, comp_type); return (NULL); } if (comp->rm_rid != rid) { if_printf(sc->hn_ifp, "RNDIS comp rid mismatch %u, " "expect %u\n", comp->rm_rid, rid); return (NULL); } /* All pass! */ *comp_len0 = comp_len; return (comp); } static int hn_rndis_query(struct hn_softc *sc, uint32_t oid, const void *idata, size_t idlen, void *odata, size_t *odlen0) { struct rndis_query_req *req; const struct rndis_query_comp *comp; struct vmbus_xact *xact; size_t reqlen, odlen = *odlen0, comp_len; int error, ofs; uint32_t rid; reqlen = sizeof(*req) + idlen; xact = vmbus_xact_get(sc->hn_xact, reqlen); if (xact == NULL) { if_printf(sc->hn_ifp, "no xact for RNDIS query 0x%08x\n", oid); return (ENXIO); } rid = hn_rndis_rid(sc); req = vmbus_xact_req_data(xact); req->rm_type = REMOTE_NDIS_QUERY_MSG; req->rm_len = reqlen; req->rm_rid = rid; req->rm_oid = oid; /* * XXX * This is _not_ RNDIS Spec conforming: * "This MUST be set to 0 when there is no input data * associated with the OID." * * If this field was set to 0 according to the RNDIS Spec, * Hyper-V would set non-SUCCESS status in the query * completion. */ req->rm_infobufoffset = RNDIS_QUERY_REQ_INFOBUFOFFSET; if (idlen > 0) { req->rm_infobuflen = idlen; /* Input data immediately follows RNDIS query. */ memcpy(req + 1, idata, idlen); } comp_len = sizeof(*comp) + odlen; comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len, REMOTE_NDIS_QUERY_CMPLT); if (comp == NULL) { if_printf(sc->hn_ifp, "exec RNDIS query 0x%08x failed\n", oid); error = EIO; goto done; } if (comp->rm_status != RNDIS_STATUS_SUCCESS) { if_printf(sc->hn_ifp, "RNDIS query 0x%08x failed: " "status 0x%08x\n", oid, comp->rm_status); error = EIO; goto done; } if (comp->rm_infobuflen == 0 || comp->rm_infobufoffset == 0) { /* No output data! */ if_printf(sc->hn_ifp, "RNDIS query 0x%08x, no data\n", oid); *odlen0 = 0; error = 0; goto done; } /* * Check output data length and offset. */ /* ofs is the offset from the beginning of comp. */ ofs = RNDIS_QUERY_COMP_INFOBUFABS(comp->rm_infobufoffset); if (ofs < sizeof(*comp) || ofs + comp->rm_infobuflen > comp_len) { if_printf(sc->hn_ifp, "RNDIS query invalid comp ib off/len, " "%u/%u\n", comp->rm_infobufoffset, comp->rm_infobuflen); error = EINVAL; goto done; } /* * Save output data. */ if (comp->rm_infobuflen < odlen) odlen = comp->rm_infobuflen; memcpy(odata, ((const uint8_t *)comp) + ofs, odlen); *odlen0 = odlen; error = 0; done: vmbus_xact_put(xact); return (error); } static int hn_rndis_get_rsscaps(struct hn_softc *sc, int *rxr_cnt) { struct ndis_rss_caps in, caps; size_t caps_len; int error; /* * Only NDIS 6.30+ is supported. */ KASSERT(sc->hn_ndis_ver >= NDIS_VERSION_6_30, ("NDIS 6.30+ is required, NDIS version 0x%08x", sc->hn_ndis_ver)); *rxr_cnt = 0; memset(&in, 0, sizeof(in)); in.ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_CAPS; in.ndis_hdr.ndis_rev = NDIS_RSS_CAPS_REV_2; in.ndis_hdr.ndis_size = NDIS_RSS_CAPS_SIZE; caps_len = NDIS_RSS_CAPS_SIZE; error = hn_rndis_query(sc, OID_GEN_RECEIVE_SCALE_CAPABILITIES, &in, NDIS_RSS_CAPS_SIZE, &caps, &caps_len); if (error) return (error); if (caps_len < NDIS_RSS_CAPS_SIZE_6_0) { if_printf(sc->hn_ifp, "invalid NDIS RSS caps len %zu", caps_len); return (EINVAL); } if (caps.ndis_nrxr == 0) { if_printf(sc->hn_ifp, "0 RX rings!?\n"); return (EINVAL); } *rxr_cnt = caps.ndis_nrxr; if (caps_len == NDIS_RSS_CAPS_SIZE) { if (bootverbose) { if_printf(sc->hn_ifp, "RSS indirect table size %u\n", caps.ndis_nind); } } return (0); } static int hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, size_t dlen) { struct rndis_set_req *req; const struct rndis_set_comp *comp; struct vmbus_xact *xact; size_t reqlen, comp_len; uint32_t rid; int error; KASSERT(dlen > 0, ("invalid dlen %zu", dlen)); reqlen = sizeof(*req) + dlen; xact = vmbus_xact_get(sc->hn_xact, reqlen); if (xact == NULL) { if_printf(sc->hn_ifp, "no xact for RNDIS set 0x%08x\n", oid); return (ENXIO); } rid = hn_rndis_rid(sc); req = vmbus_xact_req_data(xact); req->rm_type = REMOTE_NDIS_SET_MSG; req->rm_len = reqlen; req->rm_rid = rid; req->rm_oid = oid; req->rm_infobuflen = dlen; req->rm_infobufoffset = RNDIS_SET_REQ_INFOBUFOFFSET; /* Data immediately follows RNDIS set. */ memcpy(req + 1, data, dlen); comp_len = sizeof(*comp); comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len, REMOTE_NDIS_SET_CMPLT); if (comp == NULL) { if_printf(sc->hn_ifp, "exec RNDIS set 0x%08x failed\n", oid); error = EIO; goto done; } if (comp->rm_status != RNDIS_STATUS_SUCCESS) { if_printf(sc->hn_ifp, "RNDIS set 0x%08x failed: " "status 0x%08x\n", oid, comp->rm_status); error = EIO; goto done; } error = 0; done: vmbus_xact_put(xact); return (error); } static int hn_rndis_conf_offload(struct hn_softc *sc) { struct ndis_offload_params params; size_t paramsz; int error; /* NOTE: 0 means "no change" */ memset(¶ms, 0, sizeof(params)); params.ndis_hdr.ndis_type = NDIS_OBJTYPE_DEFAULT; if (sc->hn_ndis_ver < NDIS_VERSION_6_30) { params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_2; paramsz = NDIS_OFFLOAD_PARAMS_SIZE_6_1; } else { params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_3; paramsz = NDIS_OFFLOAD_PARAMS_SIZE; } params.ndis_hdr.ndis_size = paramsz; params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TXRX; params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TXRX; params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TXRX; if (sc->hn_ndis_ver >= NDIS_VERSION_6_30) { params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TXRX; params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TXRX; } params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_ON; /* XXX ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_ON */ error = hn_rndis_set(sc, OID_TCP_OFFLOAD_PARAMETERS, ¶ms, paramsz); if (error) { if_printf(sc->hn_ifp, "offload config failed: %d\n", error); } else { if (bootverbose) if_printf(sc->hn_ifp, "offload config done\n"); } return (error); } static int hn_rndis_conf_rss(struct hn_softc *sc, int nchan) { struct ndis_rssprm_toeplitz *rss = &sc->hn_rss; struct ndis_rss_params *prm = &rss->rss_params; int i, error; /* * Only NDIS 6.30+ is supported. */ KASSERT(sc->hn_ndis_ver >= NDIS_VERSION_6_30, ("NDIS 6.30+ is required, NDIS version 0x%08x", sc->hn_ndis_ver)); memset(rss, 0, sizeof(*rss)); prm->ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_PARAMS; prm->ndis_hdr.ndis_rev = NDIS_RSS_PARAMS_REV_2; prm->ndis_hdr.ndis_size = sizeof(*rss); prm->ndis_hash = NDIS_HASH_FUNCTION_TOEPLITZ | NDIS_HASH_IPV4 | NDIS_HASH_TCP_IPV4 | NDIS_HASH_IPV6 | NDIS_HASH_TCP_IPV6; /* TODO: Take ndis_rss_caps.ndis_nind into account */ prm->ndis_indsize = sizeof(rss->rss_ind); prm->ndis_indoffset = __offsetof(struct ndis_rssprm_toeplitz, rss_ind[0]); prm->ndis_keysize = sizeof(rss->rss_key); prm->ndis_keyoffset = __offsetof(struct ndis_rssprm_toeplitz, rss_key[0]); /* Setup RSS key */ memcpy(rss->rss_key, netvsc_hash_key, sizeof(rss->rss_key)); /* Setup RSS indirect table */ /* TODO: Take ndis_rss_caps.ndis_nind into account */ for (i = 0; i < NDIS_HASH_INDCNT; ++i) rss->rss_ind[i] = i % nchan; error = hn_rndis_set(sc, OID_GEN_RECEIVE_SCALE_PARAMETERS, rss, sizeof(*rss)); if (error) { if_printf(sc->hn_ifp, "RSS config failed: %d\n", error); } else { if (bootverbose) if_printf(sc->hn_ifp, "RSS config done\n"); } return (error); } static int hn_rndis_set_rxfilter(struct hn_softc *sc, uint32_t filter) { int error; error = hn_rndis_set(sc, OID_GEN_CURRENT_PACKET_FILTER, &filter, sizeof(filter)); if (error) { if_printf(sc->hn_ifp, "set RX filter 0x%08x failed: %d\n", filter, error); } else { if (bootverbose) { if_printf(sc->hn_ifp, "set RX filter 0x%08x done\n", filter); } } return (error); } /* * RNDIS filter init device */ static int hv_rf_init_device(struct hn_softc *sc) { struct rndis_init_req *req; const struct rndis_init_comp *comp; struct vmbus_xact *xact; size_t comp_len; uint32_t rid; int error; xact = vmbus_xact_get(sc->hn_xact, sizeof(*req)); if (xact == NULL) { if_printf(sc->hn_ifp, "no xact for RNDIS init\n"); return (ENXIO); } rid = hn_rndis_rid(sc); req = vmbus_xact_req_data(xact); req->rm_type = REMOTE_NDIS_INITIALIZE_MSG; req->rm_len = sizeof(*req); req->rm_rid = rid; req->rm_ver_major = RNDIS_VERSION_MAJOR; req->rm_ver_minor = RNDIS_VERSION_MINOR; req->rm_max_xfersz = HN_RNDIS_XFER_SIZE; comp_len = RNDIS_INIT_COMP_SIZE_MIN; comp = hn_rndis_xact_execute(sc, xact, rid, sizeof(*req), &comp_len, REMOTE_NDIS_INITIALIZE_CMPLT); if (comp == NULL) { if_printf(sc->hn_ifp, "exec RNDIS init failed\n"); error = EIO; goto done; } if (comp->rm_status != RNDIS_STATUS_SUCCESS) { if_printf(sc->hn_ifp, "RNDIS init failed: status 0x%08x\n", comp->rm_status); error = EIO; goto done; } if (bootverbose) { if_printf(sc->hn_ifp, "RNDIS ver %u.%u, pktsz %u, pktcnt %u, " "align %u\n", comp->rm_ver_major, comp->rm_ver_minor, comp->rm_pktmaxsz, comp->rm_pktmaxcnt, 1U << comp->rm_align); } error = 0; done: vmbus_xact_put(xact); return (error); } /* * RNDIS filter halt device */ static int hv_rf_halt_device(struct hn_softc *sc) { struct vmbus_xact *xact; struct rndis_halt_req *halt; struct hn_send_ctx sndc; size_t comp_len; xact = vmbus_xact_get(sc->hn_xact, sizeof(*halt)); if (xact == NULL) { if_printf(sc->hn_ifp, "no xact for RNDIS halt\n"); return (ENXIO); } halt = vmbus_xact_req_data(xact); halt->rm_type = REMOTE_NDIS_HALT_MSG; halt->rm_len = sizeof(*halt); halt->rm_rid = hn_rndis_rid(sc); /* No RNDIS completion; rely on NVS message send completion */ hn_send_ctx_init_simple(&sndc, hn_nvs_sent_xact, xact); hn_rndis_xact_exec1(sc, xact, sizeof(*halt), &sndc, &comp_len); vmbus_xact_put(xact); if (bootverbose) if_printf(sc->hn_ifp, "RNDIS halt done\n"); return (0); } /* * RNDIS filter on device add */ int hv_rf_on_device_add(struct hn_softc *sc, void *additl_info, int *nchan0, struct hn_rx_ring *rxr) { int ret; netvsc_device_info *dev_info = (netvsc_device_info *)additl_info; device_t dev = sc->hn_dev; struct hn_nvs_subch_req *req; const struct hn_nvs_subch_resp *resp; size_t resp_len; struct vmbus_xact *xact = NULL; uint32_t status, nsubch; int nchan = *nchan0; int rxr_cnt; /* * Let the inner driver handle this first to create the netvsc channel * NOTE! Once the channel is created, we may get a receive callback * (hv_rf_on_receive()) before this call is completed. * Note: Earlier code used a function pointer here. */ ret = hv_nv_on_device_add(sc, rxr); if (ret != 0) return (ret); /* * Initialize the rndis device */ /* Send the rndis initialization message */ ret = hv_rf_init_device(sc); if (ret != 0) { /* * TODO: If rndis init failed, we will need to shut down * the channel */ } /* Get the mac address */ ret = hv_rf_query_device_mac(sc, dev_info->mac_addr); if (ret != 0) { /* TODO: shut down rndis device and the channel */ } /* Configure NDIS offload settings */ hn_rndis_conf_offload(sc); hv_rf_query_device_link_status(sc, &dev_info->link_state); if (sc->hn_ndis_ver < NDIS_VERSION_6_30 || nchan == 1) { /* * Either RSS is not supported, or multiple RX/TX rings * are not requested. */ *nchan0 = 1; return (0); } /* * Get RSS capabilities, e.g. # of RX rings, and # of indirect * table entries. */ ret = hn_rndis_get_rsscaps(sc, &rxr_cnt); if (ret) { /* No RSS; this is benign. */ *nchan0 = 1; return (0); } if (nchan > rxr_cnt) nchan = rxr_cnt; if_printf(sc->hn_ifp, "RX rings offered %u, requested %d\n", rxr_cnt, nchan); if (nchan == 1) { device_printf(dev, "only 1 channel is supported, no vRSS\n"); goto out; } /* * Ask NVS to allocate sub-channels. */ xact = vmbus_xact_get(sc->hn_xact, sizeof(*req)); if (xact == NULL) { if_printf(sc->hn_ifp, "no xact for nvs subch req\n"); ret = ENXIO; goto out; } req = vmbus_xact_req_data(xact); req->nvs_type = HN_NVS_TYPE_SUBCH_REQ; req->nvs_op = HN_NVS_SUBCH_OP_ALLOC; req->nvs_nsubch = nchan - 1; resp_len = sizeof(*resp); resp = hn_nvs_xact_execute(sc, xact, req, sizeof(*req), &resp_len, HN_NVS_TYPE_SUBCH_RESP); if (resp == NULL) { if_printf(sc->hn_ifp, "exec subch failed\n"); ret = EIO; goto out; } status = resp->nvs_status; nsubch = resp->nvs_nsubch; vmbus_xact_put(xact); xact = NULL; if (status != HN_NVS_STATUS_OK) { if_printf(sc->hn_ifp, "subch req failed: %x\n", status); ret = EIO; goto out; } if (nsubch > nchan - 1) { if_printf(sc->hn_ifp, "%u subchans are allocated, requested %u\n", nsubch, nchan - 1); nsubch = nchan - 1; } nchan = nsubch + 1; ret = hn_rndis_conf_rss(sc, nchan); if (ret != 0) *nchan0 = 1; else *nchan0 = nchan; out: if (xact != NULL) vmbus_xact_put(xact); return (ret); } /* * RNDIS filter on device remove */ int hv_rf_on_device_remove(struct hn_softc *sc) { int ret; /* Halt and release the rndis device */ ret = hv_rf_halt_device(sc); /* Pass control to inner driver to remove the device */ ret |= hv_nv_on_device_remove(sc); return (ret); } /* * RNDIS filter on open */ int hv_rf_on_open(struct hn_softc *sc) { uint32_t filter; /* XXX */ if (hv_promisc_mode != 1) { filter = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_ALL_MULTICAST | NDIS_PACKET_TYPE_DIRECTED; } else { filter = NDIS_PACKET_TYPE_PROMISCUOUS; } return (hn_rndis_set_rxfilter(sc, filter)); } /* * RNDIS filter on close */ int hv_rf_on_close(struct hn_softc *sc) { return (hn_rndis_set_rxfilter(sc, 0)); } void hv_rf_channel_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr) { netvsc_channel_rollup(rxr, txr); } Index: projects/clang390-import/sys/dev/hyperv/netvsc/hv_rndis_filter.h =================================================================== --- projects/clang390-import/sys/dev/hyperv/netvsc/hv_rndis_filter.h (revision 305430) +++ projects/clang390-import/sys/dev/hyperv/netvsc/hv_rndis_filter.h (revision 305431) @@ -1,53 +1,53 @@ /*- * Copyright (c) 2009-2012,2016 Microsoft Corp. * Copyright (c) 2010-2012 Citrix Inc. * Copyright (c) 2012 NetApp Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef __HV_RNDIS_FILTER_H__ #define __HV_RNDIS_FILTER_H__ #include #include #include /* * Externs */ struct hn_rx_ring; -int hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr, +void hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr, const void *data, int dlen); void hv_rf_channel_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr); int hv_rf_on_device_add(struct hn_softc *sc, void *additl_info, int *nchan, struct hn_rx_ring *rxr); int hv_rf_on_device_remove(struct hn_softc *sc); int hv_rf_on_open(struct hn_softc *sc); int hv_rf_on_close(struct hn_softc *sc); #endif /* __HV_RNDIS_FILTER_H__ */ Index: projects/clang390-import/sys/dev/hyperv/vmbus/vmbus_chan.c =================================================================== --- projects/clang390-import/sys/dev/hyperv/vmbus/vmbus_chan.c (revision 305430) +++ projects/clang390-import/sys/dev/hyperv/vmbus/vmbus_chan.c (revision 305431) @@ -1,1426 +1,1452 @@ /*- * Copyright (c) 2009-2012,2016 Microsoft Corp. * Copyright (c) 2012 NetApp Inc. * Copyright (c) 2012 Citrix Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void vmbus_chan_update_evtflagcnt( struct vmbus_softc *, const struct vmbus_channel *); static void vmbus_chan_close_internal( struct vmbus_channel *); static int vmbus_chan_sysctl_mnf(SYSCTL_HANDLER_ARGS); static void vmbus_chan_sysctl_create( struct vmbus_channel *); static struct vmbus_channel *vmbus_chan_alloc(struct vmbus_softc *); static void vmbus_chan_free(struct vmbus_channel *); static int vmbus_chan_add(struct vmbus_channel *); static void vmbus_chan_cpu_default(struct vmbus_channel *); static void vmbus_chan_task(void *, int); static void vmbus_chan_task_nobatch(void *, int); static void vmbus_chan_detach_task(void *, int); static void vmbus_chan_msgproc_choffer(struct vmbus_softc *, const struct vmbus_message *); static void vmbus_chan_msgproc_chrescind( struct vmbus_softc *, const struct vmbus_message *); /* * Vmbus channel message processing. */ static const vmbus_chanmsg_proc_t vmbus_chan_msgprocs[VMBUS_CHANMSG_TYPE_MAX] = { VMBUS_CHANMSG_PROC(CHOFFER, vmbus_chan_msgproc_choffer), VMBUS_CHANMSG_PROC(CHRESCIND, vmbus_chan_msgproc_chrescind), VMBUS_CHANMSG_PROC_WAKEUP(CHOPEN_RESP), VMBUS_CHANMSG_PROC_WAKEUP(GPADL_CONNRESP), VMBUS_CHANMSG_PROC_WAKEUP(GPADL_DISCONNRESP) }; /* * Notify host that there are data pending on our TX bufring. */ static __inline void vmbus_chan_signal_tx(const struct vmbus_channel *chan) { atomic_set_long(chan->ch_evtflag, chan->ch_evtflag_mask); if (chan->ch_txflags & VMBUS_CHAN_TXF_HASMNF) atomic_set_int(chan->ch_montrig, chan->ch_montrig_mask); else hypercall_signal_event(chan->ch_monprm_dma.hv_paddr); } static int vmbus_chan_sysctl_mnf(SYSCTL_HANDLER_ARGS) { struct vmbus_channel *chan = arg1; int mnf = 0; if (chan->ch_txflags & VMBUS_CHAN_TXF_HASMNF) mnf = 1; return sysctl_handle_int(oidp, &mnf, 0, req); } static void vmbus_chan_sysctl_create(struct vmbus_channel *chan) { struct sysctl_oid *ch_tree, *chid_tree, *br_tree; struct sysctl_ctx_list *ctx; uint32_t ch_id; char name[16]; /* * Add sysctl nodes related to this channel to this * channel's sysctl ctx, so that they can be destroyed * independently upon close of this channel, which can * happen even if the device is not detached. */ ctx = &chan->ch_sysctl_ctx; sysctl_ctx_init(ctx); /* * Create dev.NAME.UNIT.channel tree. */ ch_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(chan->ch_dev)), OID_AUTO, "channel", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); if (ch_tree == NULL) return; /* * Create dev.NAME.UNIT.channel.CHANID tree. */ if (VMBUS_CHAN_ISPRIMARY(chan)) ch_id = chan->ch_id; else ch_id = chan->ch_prichan->ch_id; snprintf(name, sizeof(name), "%d", ch_id); chid_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(ch_tree), OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); if (chid_tree == NULL) return; if (!VMBUS_CHAN_ISPRIMARY(chan)) { /* * Create dev.NAME.UNIT.channel.CHANID.sub tree. */ ch_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO, "sub", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); if (ch_tree == NULL) return; /* * Create dev.NAME.UNIT.channel.CHANID.sub.SUBIDX tree. * * NOTE: * chid_tree is changed to this new sysctl tree. */ snprintf(name, sizeof(name), "%d", chan->ch_subidx); chid_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(ch_tree), OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); if (chid_tree == NULL) return; SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO, "chanid", CTLFLAG_RD, &chan->ch_id, 0, "channel id"); } SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO, "cpu", CTLFLAG_RD, &chan->ch_cpuid, 0, "owner CPU id"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO, "mnf", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, chan, 0, vmbus_chan_sysctl_mnf, "I", "has monitor notification facilities"); br_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO, "br", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); if (br_tree != NULL) { /* * Create sysctl tree for RX bufring. */ vmbus_br_sysctl_create(ctx, br_tree, &chan->ch_rxbr.rxbr, "rx"); /* * Create sysctl tree for TX bufring. */ vmbus_br_sysctl_create(ctx, br_tree, &chan->ch_txbr.txbr, "tx"); } } int vmbus_chan_open(struct vmbus_channel *chan, int txbr_size, int rxbr_size, const void *udata, int udlen, vmbus_chan_callback_t cb, void *cbarg) { struct vmbus_softc *sc = chan->ch_vmbus; const struct vmbus_chanmsg_chopen_resp *resp; const struct vmbus_message *msg; struct vmbus_chanmsg_chopen *req; struct vmbus_msghc *mh; uint32_t status; int error; uint8_t *br; if (udlen > VMBUS_CHANMSG_CHOPEN_UDATA_SIZE) { device_printf(sc->vmbus_dev, "invalid udata len %d for chan%u\n", udlen, chan->ch_id); return EINVAL; } KASSERT((txbr_size & PAGE_MASK) == 0, ("send bufring size is not multiple page")); KASSERT((rxbr_size & PAGE_MASK) == 0, ("recv bufring size is not multiple page")); if (atomic_testandset_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED_SHIFT)) panic("double-open chan%u", chan->ch_id); chan->ch_cb = cb; chan->ch_cbarg = cbarg; vmbus_chan_update_evtflagcnt(sc, chan); chan->ch_tq = VMBUS_PCPU_GET(chan->ch_vmbus, event_tq, chan->ch_cpuid); if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD) TASK_INIT(&chan->ch_task, 0, vmbus_chan_task, chan); else TASK_INIT(&chan->ch_task, 0, vmbus_chan_task_nobatch, chan); /* * Allocate the TX+RX bufrings. * XXX should use ch_dev dtag */ br = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev), PAGE_SIZE, 0, txbr_size + rxbr_size, &chan->ch_bufring_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO); if (br == NULL) { device_printf(sc->vmbus_dev, "bufring allocation failed\n"); error = ENOMEM; goto failed; } chan->ch_bufring = br; /* TX bufring comes first */ vmbus_txbr_setup(&chan->ch_txbr, br, txbr_size); /* RX bufring immediately follows TX bufring */ vmbus_rxbr_setup(&chan->ch_rxbr, br + txbr_size, rxbr_size); /* Create sysctl tree for this channel */ vmbus_chan_sysctl_create(chan); /* * Connect the bufrings, both RX and TX, to this channel. */ error = vmbus_chan_gpadl_connect(chan, chan->ch_bufring_dma.hv_paddr, txbr_size + rxbr_size, &chan->ch_bufring_gpadl); if (error) { device_printf(sc->vmbus_dev, "failed to connect bufring GPADL to chan%u\n", chan->ch_id); goto failed; } /* * Open channel w/ the bufring GPADL on the target CPU. */ mh = vmbus_msghc_get(sc, sizeof(*req)); if (mh == NULL) { device_printf(sc->vmbus_dev, "can not get msg hypercall for chopen(chan%u)\n", chan->ch_id); error = ENXIO; goto failed; } req = vmbus_msghc_dataptr(mh); req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHOPEN; req->chm_chanid = chan->ch_id; req->chm_openid = chan->ch_id; req->chm_gpadl = chan->ch_bufring_gpadl; req->chm_vcpuid = chan->ch_vcpuid; req->chm_txbr_pgcnt = txbr_size >> PAGE_SHIFT; if (udlen > 0) memcpy(req->chm_udata, udata, udlen); error = vmbus_msghc_exec(sc, mh); if (error) { device_printf(sc->vmbus_dev, "chopen(chan%u) msg hypercall exec failed: %d\n", chan->ch_id, error); vmbus_msghc_put(sc, mh); goto failed; } msg = vmbus_msghc_wait_result(sc, mh); resp = (const struct vmbus_chanmsg_chopen_resp *)msg->msg_data; status = resp->chm_status; vmbus_msghc_put(sc, mh); if (status == 0) { if (bootverbose) { device_printf(sc->vmbus_dev, "chan%u opened\n", chan->ch_id); } return 0; } device_printf(sc->vmbus_dev, "failed to open chan%u\n", chan->ch_id); error = ENXIO; failed: if (chan->ch_bufring_gpadl) { vmbus_chan_gpadl_disconnect(chan, chan->ch_bufring_gpadl); chan->ch_bufring_gpadl = 0; } if (chan->ch_bufring != NULL) { hyperv_dmamem_free(&chan->ch_bufring_dma, chan->ch_bufring); chan->ch_bufring = NULL; } atomic_clear_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED); return error; } int vmbus_chan_gpadl_connect(struct vmbus_channel *chan, bus_addr_t paddr, int size, uint32_t *gpadl0) { struct vmbus_softc *sc = chan->ch_vmbus; struct vmbus_msghc *mh; struct vmbus_chanmsg_gpadl_conn *req; const struct vmbus_message *msg; size_t reqsz; uint32_t gpadl, status; int page_count, range_len, i, cnt, error; uint64_t page_id; /* * Preliminary checks. */ KASSERT((size & PAGE_MASK) == 0, ("invalid GPA size %d, not multiple page size", size)); page_count = size >> PAGE_SHIFT; KASSERT((paddr & PAGE_MASK) == 0, ("GPA is not page aligned %jx", (uintmax_t)paddr)); page_id = paddr >> PAGE_SHIFT; range_len = __offsetof(struct vmbus_gpa_range, gpa_page[page_count]); /* * We don't support multiple GPA ranges. */ if (range_len > UINT16_MAX) { device_printf(sc->vmbus_dev, "GPA too large, %d pages\n", page_count); return EOPNOTSUPP; } /* * Allocate GPADL id. */ gpadl = vmbus_gpadl_alloc(sc); *gpadl0 = gpadl; /* * Connect this GPADL to the target channel. * * NOTE: * Since each message can only hold small set of page * addresses, several messages may be required to * complete the connection. */ if (page_count > VMBUS_CHANMSG_GPADL_CONN_PGMAX) cnt = VMBUS_CHANMSG_GPADL_CONN_PGMAX; else cnt = page_count; page_count -= cnt; reqsz = __offsetof(struct vmbus_chanmsg_gpadl_conn, chm_range.gpa_page[cnt]); mh = vmbus_msghc_get(sc, reqsz); if (mh == NULL) { device_printf(sc->vmbus_dev, "can not get msg hypercall for gpadl->chan%u\n", chan->ch_id); return EIO; } req = vmbus_msghc_dataptr(mh); req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_CONN; req->chm_chanid = chan->ch_id; req->chm_gpadl = gpadl; req->chm_range_len = range_len; req->chm_range_cnt = 1; req->chm_range.gpa_len = size; req->chm_range.gpa_ofs = 0; for (i = 0; i < cnt; ++i) req->chm_range.gpa_page[i] = page_id++; error = vmbus_msghc_exec(sc, mh); if (error) { device_printf(sc->vmbus_dev, "gpadl->chan%u msg hypercall exec failed: %d\n", chan->ch_id, error); vmbus_msghc_put(sc, mh); return error; } while (page_count > 0) { struct vmbus_chanmsg_gpadl_subconn *subreq; if (page_count > VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX) cnt = VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX; else cnt = page_count; page_count -= cnt; reqsz = __offsetof(struct vmbus_chanmsg_gpadl_subconn, chm_gpa_page[cnt]); vmbus_msghc_reset(mh, reqsz); subreq = vmbus_msghc_dataptr(mh); subreq->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_SUBCONN; subreq->chm_gpadl = gpadl; for (i = 0; i < cnt; ++i) subreq->chm_gpa_page[i] = page_id++; vmbus_msghc_exec_noresult(mh); } KASSERT(page_count == 0, ("invalid page count %d", page_count)); msg = vmbus_msghc_wait_result(sc, mh); status = ((const struct vmbus_chanmsg_gpadl_connresp *) msg->msg_data)->chm_status; vmbus_msghc_put(sc, mh); if (status != 0) { device_printf(sc->vmbus_dev, "gpadl->chan%u failed: " "status %u\n", chan->ch_id, status); return EIO; } else { if (bootverbose) { device_printf(sc->vmbus_dev, "gpadl->chan%u " "succeeded\n", chan->ch_id); } } return 0; } /* * Disconnect the GPA from the target channel */ int vmbus_chan_gpadl_disconnect(struct vmbus_channel *chan, uint32_t gpadl) { struct vmbus_softc *sc = chan->ch_vmbus; struct vmbus_msghc *mh; struct vmbus_chanmsg_gpadl_disconn *req; int error; mh = vmbus_msghc_get(sc, sizeof(*req)); if (mh == NULL) { device_printf(sc->vmbus_dev, "can not get msg hypercall for gpa x->chan%u\n", chan->ch_id); return EBUSY; } req = vmbus_msghc_dataptr(mh); req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_DISCONN; req->chm_chanid = chan->ch_id; req->chm_gpadl = gpadl; error = vmbus_msghc_exec(sc, mh); if (error) { device_printf(sc->vmbus_dev, "gpa x->chan%u msg hypercall exec failed: %d\n", chan->ch_id, error); vmbus_msghc_put(sc, mh); return error; } vmbus_msghc_wait_result(sc, mh); /* Discard result; no useful information */ vmbus_msghc_put(sc, mh); return 0; } static void vmbus_chan_close_internal(struct vmbus_channel *chan) { struct vmbus_softc *sc = chan->ch_vmbus; struct vmbus_msghc *mh; struct vmbus_chanmsg_chclose *req; struct taskqueue *tq = chan->ch_tq; int error; /* TODO: stringent check */ atomic_clear_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED); /* * Free this channel's sysctl tree attached to its device's * sysctl tree. */ sysctl_ctx_free(&chan->ch_sysctl_ctx); /* * Set ch_tq to NULL to avoid more requests be scheduled. * XXX pretty broken; need rework. */ chan->ch_tq = NULL; taskqueue_drain(tq, &chan->ch_task); chan->ch_cb = NULL; /* * Close this channel. */ mh = vmbus_msghc_get(sc, sizeof(*req)); if (mh == NULL) { device_printf(sc->vmbus_dev, "can not get msg hypercall for chclose(chan%u)\n", chan->ch_id); return; } req = vmbus_msghc_dataptr(mh); req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHCLOSE; req->chm_chanid = chan->ch_id; error = vmbus_msghc_exec_noresult(mh); vmbus_msghc_put(sc, mh); if (error) { device_printf(sc->vmbus_dev, "chclose(chan%u) msg hypercall exec failed: %d\n", chan->ch_id, error); return; } else if (bootverbose) { device_printf(sc->vmbus_dev, "close chan%u\n", chan->ch_id); } /* * Disconnect the TX+RX bufrings from this channel. */ if (chan->ch_bufring_gpadl) { vmbus_chan_gpadl_disconnect(chan, chan->ch_bufring_gpadl); chan->ch_bufring_gpadl = 0; } /* * Destroy the TX+RX bufrings. */ if (chan->ch_bufring != NULL) { hyperv_dmamem_free(&chan->ch_bufring_dma, chan->ch_bufring); chan->ch_bufring = NULL; } } /* * Caller should make sure that all sub-channels have * been added to 'chan' and all to-be-closed channels * are not being opened. */ void vmbus_chan_close(struct vmbus_channel *chan) { int subchan_cnt; if (!VMBUS_CHAN_ISPRIMARY(chan)) { /* * Sub-channel is closed when its primary channel * is closed; done. */ return; } /* * Close all sub-channels, if any. */ subchan_cnt = chan->ch_subchan_cnt; if (subchan_cnt > 0) { struct vmbus_channel **subchan; int i; subchan = vmbus_subchan_get(chan, subchan_cnt); for (i = 0; i < subchan_cnt; ++i) vmbus_chan_close_internal(subchan[i]); vmbus_subchan_rel(subchan, subchan_cnt); } /* Then close the primary channel. */ vmbus_chan_close_internal(chan); } int vmbus_chan_send(struct vmbus_channel *chan, uint16_t type, uint16_t flags, void *data, int dlen, uint64_t xactid) { struct vmbus_chanpkt pkt; int pktlen, pad_pktlen, hlen, error; uint64_t pad = 0; struct iovec iov[3]; boolean_t send_evt; hlen = sizeof(pkt); pktlen = hlen + dlen; pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen); KASSERT(pad_pktlen <= vmbus_txbr_maxpktsz(&chan->ch_txbr), ("invalid packet size %d", pad_pktlen)); pkt.cp_hdr.cph_type = type; pkt.cp_hdr.cph_flags = flags; VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen); VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen); pkt.cp_hdr.cph_xactid = xactid; iov[0].iov_base = &pkt; iov[0].iov_len = hlen; iov[1].iov_base = data; iov[1].iov_len = dlen; iov[2].iov_base = &pad; iov[2].iov_len = pad_pktlen - pktlen; error = vmbus_txbr_write(&chan->ch_txbr, iov, 3, &send_evt); if (!error && send_evt) vmbus_chan_signal_tx(chan); return error; } int vmbus_chan_send_sglist(struct vmbus_channel *chan, struct vmbus_gpa sg[], int sglen, void *data, int dlen, uint64_t xactid) { struct vmbus_chanpkt_sglist pkt; int pktlen, pad_pktlen, hlen, error; struct iovec iov[4]; boolean_t send_evt; uint64_t pad = 0; hlen = __offsetof(struct vmbus_chanpkt_sglist, cp_gpa[sglen]); pktlen = hlen + dlen; pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen); KASSERT(pad_pktlen <= vmbus_txbr_maxpktsz(&chan->ch_txbr), ("invalid packet size %d", pad_pktlen)); pkt.cp_hdr.cph_type = VMBUS_CHANPKT_TYPE_GPA; pkt.cp_hdr.cph_flags = VMBUS_CHANPKT_FLAG_RC; VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen); VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen); pkt.cp_hdr.cph_xactid = xactid; pkt.cp_rsvd = 0; pkt.cp_gpa_cnt = sglen; iov[0].iov_base = &pkt; iov[0].iov_len = sizeof(pkt); iov[1].iov_base = sg; iov[1].iov_len = sizeof(struct vmbus_gpa) * sglen; iov[2].iov_base = data; iov[2].iov_len = dlen; iov[3].iov_base = &pad; iov[3].iov_len = pad_pktlen - pktlen; error = vmbus_txbr_write(&chan->ch_txbr, iov, 4, &send_evt); if (!error && send_evt) vmbus_chan_signal_tx(chan); return error; } int vmbus_chan_send_prplist(struct vmbus_channel *chan, struct vmbus_gpa_range *prp, int prp_cnt, void *data, int dlen, uint64_t xactid) { struct vmbus_chanpkt_prplist pkt; int pktlen, pad_pktlen, hlen, error; struct iovec iov[4]; boolean_t send_evt; uint64_t pad = 0; hlen = __offsetof(struct vmbus_chanpkt_prplist, cp_range[0].gpa_page[prp_cnt]); pktlen = hlen + dlen; pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen); KASSERT(pad_pktlen <= vmbus_txbr_maxpktsz(&chan->ch_txbr), ("invalid packet size %d", pad_pktlen)); pkt.cp_hdr.cph_type = VMBUS_CHANPKT_TYPE_GPA; pkt.cp_hdr.cph_flags = VMBUS_CHANPKT_FLAG_RC; VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen); VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen); pkt.cp_hdr.cph_xactid = xactid; pkt.cp_rsvd = 0; pkt.cp_range_cnt = 1; iov[0].iov_base = &pkt; iov[0].iov_len = sizeof(pkt); iov[1].iov_base = prp; iov[1].iov_len = __offsetof(struct vmbus_gpa_range, gpa_page[prp_cnt]); iov[2].iov_base = data; iov[2].iov_len = dlen; iov[3].iov_base = &pad; iov[3].iov_len = pad_pktlen - pktlen; error = vmbus_txbr_write(&chan->ch_txbr, iov, 4, &send_evt); if (!error && send_evt) vmbus_chan_signal_tx(chan); return error; } int vmbus_chan_recv(struct vmbus_channel *chan, void *data, int *dlen0, uint64_t *xactid) { struct vmbus_chanpkt_hdr pkt; int error, dlen, hlen; error = vmbus_rxbr_peek(&chan->ch_rxbr, &pkt, sizeof(pkt)); if (error) - return error; + return (error); + if (__predict_false(pkt.cph_hlen < VMBUS_CHANPKT_HLEN_MIN)) { + device_printf(chan->ch_dev, "invalid hlen %u\n", + pkt.cph_hlen); + /* XXX this channel is dead actually. */ + return (EIO); + } + if (__predict_false(pkt.cph_hlen > pkt.cph_tlen)) { + device_printf(chan->ch_dev, "invalid hlen %u and tlen %u\n", + pkt.cph_hlen, pkt.cph_tlen); + /* XXX this channel is dead actually. */ + return (EIO); + } + hlen = VMBUS_CHANPKT_GETLEN(pkt.cph_hlen); dlen = VMBUS_CHANPKT_GETLEN(pkt.cph_tlen) - hlen; if (*dlen0 < dlen) { /* Return the size of this packet's data. */ *dlen0 = dlen; - return ENOBUFS; + return (ENOBUFS); } *xactid = pkt.cph_xactid; *dlen0 = dlen; /* Skip packet header */ error = vmbus_rxbr_read(&chan->ch_rxbr, data, dlen, hlen); KASSERT(!error, ("vmbus_rxbr_read failed")); - return 0; + return (0); } int vmbus_chan_recv_pkt(struct vmbus_channel *chan, struct vmbus_chanpkt_hdr *pkt0, int *pktlen0) { struct vmbus_chanpkt_hdr pkt; int error, pktlen; error = vmbus_rxbr_peek(&chan->ch_rxbr, &pkt, sizeof(pkt)); if (error) - return error; + return (error); + if (__predict_false(pkt.cph_hlen < VMBUS_CHANPKT_HLEN_MIN)) { + device_printf(chan->ch_dev, "invalid hlen %u\n", + pkt.cph_hlen); + /* XXX this channel is dead actually. */ + return (EIO); + } + if (__predict_false(pkt.cph_hlen > pkt.cph_tlen)) { + device_printf(chan->ch_dev, "invalid hlen %u and tlen %u\n", + pkt.cph_hlen, pkt.cph_tlen); + /* XXX this channel is dead actually. */ + return (EIO); + } + pktlen = VMBUS_CHANPKT_GETLEN(pkt.cph_tlen); if (*pktlen0 < pktlen) { /* Return the size of this packet. */ *pktlen0 = pktlen; - return ENOBUFS; + return (ENOBUFS); } *pktlen0 = pktlen; /* Include packet header */ error = vmbus_rxbr_read(&chan->ch_rxbr, pkt0, pktlen, 0); KASSERT(!error, ("vmbus_rxbr_read failed")); - return 0; + return (0); } static void vmbus_chan_task(void *xchan, int pending __unused) { struct vmbus_channel *chan = xchan; vmbus_chan_callback_t cb = chan->ch_cb; void *cbarg = chan->ch_cbarg; /* * Optimize host to guest signaling by ensuring: * 1. While reading the channel, we disable interrupts from * host. * 2. Ensure that we process all posted messages from the host * before returning from this callback. * 3. Once we return, enable signaling from the host. Once this * state is set we check to see if additional packets are * available to read. In this case we repeat the process. * * NOTE: Interrupt has been disabled in the ISR. */ for (;;) { uint32_t left; cb(chan, cbarg); left = vmbus_rxbr_intr_unmask(&chan->ch_rxbr); if (left == 0) { /* No more data in RX bufring; done */ break; } vmbus_rxbr_intr_mask(&chan->ch_rxbr); } } static void vmbus_chan_task_nobatch(void *xchan, int pending __unused) { struct vmbus_channel *chan = xchan; chan->ch_cb(chan, chan->ch_cbarg); } static __inline void vmbus_event_flags_proc(struct vmbus_softc *sc, volatile u_long *event_flags, int flag_cnt) { int f; for (f = 0; f < flag_cnt; ++f) { uint32_t chid_base; u_long flags; int chid_ofs; if (event_flags[f] == 0) continue; flags = atomic_swap_long(&event_flags[f], 0); chid_base = f << VMBUS_EVTFLAG_SHIFT; while ((chid_ofs = ffsl(flags)) != 0) { struct vmbus_channel *chan; --chid_ofs; /* NOTE: ffsl is 1-based */ flags &= ~(1UL << chid_ofs); chan = sc->vmbus_chmap[chid_base + chid_ofs]; /* if channel is closed or closing */ if (chan == NULL || chan->ch_tq == NULL) continue; if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD) vmbus_rxbr_intr_mask(&chan->ch_rxbr); taskqueue_enqueue(chan->ch_tq, &chan->ch_task); } } } void vmbus_event_proc(struct vmbus_softc *sc, int cpu) { struct vmbus_evtflags *eventf; /* * On Host with Win8 or above, the event page can be checked directly * to get the id of the channel that has the pending interrupt. */ eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE; vmbus_event_flags_proc(sc, eventf->evt_flags, VMBUS_PCPU_GET(sc, event_flags_cnt, cpu)); } void vmbus_event_proc_compat(struct vmbus_softc *sc, int cpu) { struct vmbus_evtflags *eventf; eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE; if (atomic_testandclear_long(&eventf->evt_flags[0], 0)) { vmbus_event_flags_proc(sc, sc->vmbus_rx_evtflags, VMBUS_CHAN_MAX_COMPAT >> VMBUS_EVTFLAG_SHIFT); } } static void vmbus_chan_update_evtflagcnt(struct vmbus_softc *sc, const struct vmbus_channel *chan) { volatile int *flag_cnt_ptr; int flag_cnt; flag_cnt = (chan->ch_id / VMBUS_EVTFLAG_LEN) + 1; flag_cnt_ptr = VMBUS_PCPU_PTR(sc, event_flags_cnt, chan->ch_cpuid); for (;;) { int old_flag_cnt; old_flag_cnt = *flag_cnt_ptr; if (old_flag_cnt >= flag_cnt) break; if (atomic_cmpset_int(flag_cnt_ptr, old_flag_cnt, flag_cnt)) { if (bootverbose) { device_printf(sc->vmbus_dev, "channel%u update cpu%d flag_cnt to %d\n", chan->ch_id, chan->ch_cpuid, flag_cnt); } break; } } } static struct vmbus_channel * vmbus_chan_alloc(struct vmbus_softc *sc) { struct vmbus_channel *chan; chan = malloc(sizeof(*chan), M_DEVBUF, M_WAITOK | M_ZERO); chan->ch_monprm = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev), HYPERCALL_PARAM_ALIGN, 0, sizeof(struct hyperv_mon_param), &chan->ch_monprm_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO); if (chan->ch_monprm == NULL) { device_printf(sc->vmbus_dev, "monprm alloc failed\n"); free(chan, M_DEVBUF); return NULL; } chan->ch_vmbus = sc; mtx_init(&chan->ch_subchan_lock, "vmbus subchan", NULL, MTX_DEF); TAILQ_INIT(&chan->ch_subchans); TASK_INIT(&chan->ch_detach_task, 0, vmbus_chan_detach_task, chan); vmbus_rxbr_init(&chan->ch_rxbr); vmbus_txbr_init(&chan->ch_txbr); return chan; } static void vmbus_chan_free(struct vmbus_channel *chan) { /* TODO: assert sub-channel list is empty */ /* TODO: asset no longer on the primary channel's sub-channel list */ /* TODO: asset no longer on the vmbus channel list */ hyperv_dmamem_free(&chan->ch_monprm_dma, chan->ch_monprm); mtx_destroy(&chan->ch_subchan_lock); vmbus_rxbr_deinit(&chan->ch_rxbr); vmbus_txbr_deinit(&chan->ch_txbr); free(chan, M_DEVBUF); } static int vmbus_chan_add(struct vmbus_channel *newchan) { struct vmbus_softc *sc = newchan->ch_vmbus; struct vmbus_channel *prichan; if (newchan->ch_id == 0) { /* * XXX * Chan0 will neither be processed nor should be offered; * skip it. */ device_printf(sc->vmbus_dev, "got chan0 offer, discard\n"); return EINVAL; } else if (newchan->ch_id >= VMBUS_CHAN_MAX) { device_printf(sc->vmbus_dev, "invalid chan%u offer\n", newchan->ch_id); return EINVAL; } sc->vmbus_chmap[newchan->ch_id] = newchan; if (bootverbose) { device_printf(sc->vmbus_dev, "chan%u subidx%u offer\n", newchan->ch_id, newchan->ch_subidx); } mtx_lock(&sc->vmbus_prichan_lock); TAILQ_FOREACH(prichan, &sc->vmbus_prichans, ch_prilink) { /* * Sub-channel will have the same type GUID and instance * GUID as its primary channel. */ if (memcmp(&prichan->ch_guid_type, &newchan->ch_guid_type, sizeof(struct hyperv_guid)) == 0 && memcmp(&prichan->ch_guid_inst, &newchan->ch_guid_inst, sizeof(struct hyperv_guid)) == 0) break; } if (VMBUS_CHAN_ISPRIMARY(newchan)) { if (prichan == NULL) { /* Install the new primary channel */ TAILQ_INSERT_TAIL(&sc->vmbus_prichans, newchan, ch_prilink); mtx_unlock(&sc->vmbus_prichan_lock); return 0; } else { mtx_unlock(&sc->vmbus_prichan_lock); device_printf(sc->vmbus_dev, "duplicated primary " "chan%u\n", newchan->ch_id); return EINVAL; } } else { /* Sub-channel */ if (prichan == NULL) { mtx_unlock(&sc->vmbus_prichan_lock); device_printf(sc->vmbus_dev, "no primary chan for " "chan%u\n", newchan->ch_id); return EINVAL; } /* * Found the primary channel for this sub-channel and * move on. * * XXX refcnt prichan */ } mtx_unlock(&sc->vmbus_prichan_lock); /* * This is a sub-channel; link it with the primary channel. */ KASSERT(!VMBUS_CHAN_ISPRIMARY(newchan), ("new channel is not sub-channel")); KASSERT(prichan != NULL, ("no primary channel")); newchan->ch_prichan = prichan; newchan->ch_dev = prichan->ch_dev; mtx_lock(&prichan->ch_subchan_lock); TAILQ_INSERT_TAIL(&prichan->ch_subchans, newchan, ch_sublink); /* * Bump up sub-channel count and notify anyone that is * interested in this sub-channel, after this sub-channel * is setup. */ prichan->ch_subchan_cnt++; mtx_unlock(&prichan->ch_subchan_lock); wakeup(prichan); return 0; } void vmbus_chan_cpu_set(struct vmbus_channel *chan, int cpu) { KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu)); if (chan->ch_vmbus->vmbus_version == VMBUS_VERSION_WS2008 || chan->ch_vmbus->vmbus_version == VMBUS_VERSION_WIN7) { /* Only cpu0 is supported */ cpu = 0; } chan->ch_cpuid = cpu; chan->ch_vcpuid = VMBUS_PCPU_GET(chan->ch_vmbus, vcpuid, cpu); if (bootverbose) { printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n", chan->ch_id, chan->ch_cpuid, chan->ch_vcpuid); } } void vmbus_chan_cpu_rr(struct vmbus_channel *chan) { static uint32_t vmbus_chan_nextcpu; int cpu; cpu = atomic_fetchadd_int(&vmbus_chan_nextcpu, 1) % mp_ncpus; vmbus_chan_cpu_set(chan, cpu); } static void vmbus_chan_cpu_default(struct vmbus_channel *chan) { /* * By default, pin the channel to cpu0. Devices having * special channel-cpu mapping requirement should call * vmbus_chan_cpu_{set,rr}(). */ vmbus_chan_cpu_set(chan, 0); } static void vmbus_chan_msgproc_choffer(struct vmbus_softc *sc, const struct vmbus_message *msg) { const struct vmbus_chanmsg_choffer *offer; struct vmbus_channel *chan; int error; offer = (const struct vmbus_chanmsg_choffer *)msg->msg_data; chan = vmbus_chan_alloc(sc); if (chan == NULL) { device_printf(sc->vmbus_dev, "allocate chan%u failed\n", offer->chm_chanid); return; } chan->ch_id = offer->chm_chanid; chan->ch_subidx = offer->chm_subidx; chan->ch_guid_type = offer->chm_chtype; chan->ch_guid_inst = offer->chm_chinst; /* Batch reading is on by default */ chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD; chan->ch_monprm->mp_connid = VMBUS_CONNID_EVENT; if (sc->vmbus_version != VMBUS_VERSION_WS2008) chan->ch_monprm->mp_connid = offer->chm_connid; if (offer->chm_flags1 & VMBUS_CHOFFER_FLAG1_HASMNF) { int trig_idx; /* * Setup MNF stuffs. */ chan->ch_txflags |= VMBUS_CHAN_TXF_HASMNF; trig_idx = offer->chm_montrig / VMBUS_MONTRIG_LEN; if (trig_idx >= VMBUS_MONTRIGS_MAX) panic("invalid monitor trigger %u", offer->chm_montrig); chan->ch_montrig = &sc->vmbus_mnf2->mnf_trigs[trig_idx].mt_pending; chan->ch_montrig_mask = 1 << (offer->chm_montrig % VMBUS_MONTRIG_LEN); } /* * Setup event flag. */ chan->ch_evtflag = &sc->vmbus_tx_evtflags[chan->ch_id >> VMBUS_EVTFLAG_SHIFT]; chan->ch_evtflag_mask = 1UL << (chan->ch_id & VMBUS_EVTFLAG_MASK); /* Select default cpu for this channel. */ vmbus_chan_cpu_default(chan); error = vmbus_chan_add(chan); if (error) { device_printf(sc->vmbus_dev, "add chan%u failed: %d\n", chan->ch_id, error); vmbus_chan_free(chan); return; } if (VMBUS_CHAN_ISPRIMARY(chan)) { /* * Add device for this primary channel. * * NOTE: * Error is ignored here; don't have much to do if error * really happens. */ vmbus_add_child(chan); } } /* * XXX pretty broken; need rework. */ static void vmbus_chan_msgproc_chrescind(struct vmbus_softc *sc, const struct vmbus_message *msg) { const struct vmbus_chanmsg_chrescind *note; struct vmbus_channel *chan; note = (const struct vmbus_chanmsg_chrescind *)msg->msg_data; if (note->chm_chanid > VMBUS_CHAN_MAX) { device_printf(sc->vmbus_dev, "invalid rescinded chan%u\n", note->chm_chanid); return; } if (bootverbose) { device_printf(sc->vmbus_dev, "chan%u rescinded\n", note->chm_chanid); } chan = sc->vmbus_chmap[note->chm_chanid]; if (chan == NULL) return; sc->vmbus_chmap[note->chm_chanid] = NULL; taskqueue_enqueue(taskqueue_thread, &chan->ch_detach_task); } static void vmbus_chan_detach_task(void *xchan, int pending __unused) { struct vmbus_channel *chan = xchan; if (VMBUS_CHAN_ISPRIMARY(chan)) { /* Only primary channel owns the device */ vmbus_delete_child(chan); /* NOTE: DO NOT free primary channel for now */ } else { struct vmbus_softc *sc = chan->ch_vmbus; struct vmbus_channel *pri_chan = chan->ch_prichan; struct vmbus_chanmsg_chfree *req; struct vmbus_msghc *mh; int error; mh = vmbus_msghc_get(sc, sizeof(*req)); if (mh == NULL) { device_printf(sc->vmbus_dev, "can not get msg hypercall for chfree(chan%u)\n", chan->ch_id); goto remove; } req = vmbus_msghc_dataptr(mh); req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE; req->chm_chanid = chan->ch_id; error = vmbus_msghc_exec_noresult(mh); vmbus_msghc_put(sc, mh); if (error) { device_printf(sc->vmbus_dev, "chfree(chan%u) failed: %d", chan->ch_id, error); /* NOTE: Move on! */ } else { if (bootverbose) { device_printf(sc->vmbus_dev, "chan%u freed\n", chan->ch_id); } } remove: mtx_lock(&pri_chan->ch_subchan_lock); TAILQ_REMOVE(&pri_chan->ch_subchans, chan, ch_sublink); KASSERT(pri_chan->ch_subchan_cnt > 0, ("invalid subchan_cnt %d", pri_chan->ch_subchan_cnt)); pri_chan->ch_subchan_cnt--; mtx_unlock(&pri_chan->ch_subchan_lock); wakeup(pri_chan); vmbus_chan_free(chan); } } /* * Detach all devices and destroy the corresponding primary channels. */ void vmbus_chan_destroy_all(struct vmbus_softc *sc) { struct vmbus_channel *chan; mtx_lock(&sc->vmbus_prichan_lock); while ((chan = TAILQ_FIRST(&sc->vmbus_prichans)) != NULL) { KASSERT(VMBUS_CHAN_ISPRIMARY(chan), ("not primary channel")); TAILQ_REMOVE(&sc->vmbus_prichans, chan, ch_prilink); mtx_unlock(&sc->vmbus_prichan_lock); vmbus_delete_child(chan); vmbus_chan_free(chan); mtx_lock(&sc->vmbus_prichan_lock); } bzero(sc->vmbus_chmap, sizeof(struct vmbus_channel *) * VMBUS_CHAN_MAX); mtx_unlock(&sc->vmbus_prichan_lock); } /* * The channel whose vcpu binding is closest to the currect vcpu will * be selected. * If no multi-channel, always select primary channel. */ struct vmbus_channel * vmbus_chan_cpu2chan(struct vmbus_channel *prichan, int cpu) { struct vmbus_channel *sel, *chan; uint32_t vcpu, sel_dist; KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpuid %d", cpu)); if (TAILQ_EMPTY(&prichan->ch_subchans)) return prichan; vcpu = VMBUS_PCPU_GET(prichan->ch_vmbus, vcpuid, cpu); #define CHAN_VCPU_DIST(ch, vcpu) \ (((ch)->ch_vcpuid > (vcpu)) ? \ ((ch)->ch_vcpuid - (vcpu)) : ((vcpu) - (ch)->ch_vcpuid)) #define CHAN_SELECT(ch) \ do { \ sel = ch; \ sel_dist = CHAN_VCPU_DIST(ch, vcpu); \ } while (0) CHAN_SELECT(prichan); mtx_lock(&prichan->ch_subchan_lock); TAILQ_FOREACH(chan, &prichan->ch_subchans, ch_sublink) { uint32_t dist; KASSERT(chan->ch_stflags & VMBUS_CHAN_ST_OPENED, ("chan%u is not opened", chan->ch_id)); if (chan->ch_vcpuid == vcpu) { /* Exact match; done */ CHAN_SELECT(chan); break; } dist = CHAN_VCPU_DIST(chan, vcpu); if (sel_dist <= dist) { /* Far or same distance; skip */ continue; } /* Select the closer channel. */ CHAN_SELECT(chan); } mtx_unlock(&prichan->ch_subchan_lock); #undef CHAN_SELECT #undef CHAN_VCPU_DIST return sel; } struct vmbus_channel ** vmbus_subchan_get(struct vmbus_channel *pri_chan, int subchan_cnt) { struct vmbus_channel **ret, *chan; int i; ret = malloc(subchan_cnt * sizeof(struct vmbus_channel *), M_TEMP, M_WAITOK); mtx_lock(&pri_chan->ch_subchan_lock); while (pri_chan->ch_subchan_cnt < subchan_cnt) mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "subch", 0); i = 0; TAILQ_FOREACH(chan, &pri_chan->ch_subchans, ch_sublink) { /* TODO: refcnt chan */ ret[i] = chan; ++i; if (i == subchan_cnt) break; } KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d", pri_chan->ch_subchan_cnt, subchan_cnt)); mtx_unlock(&pri_chan->ch_subchan_lock); return ret; } void vmbus_subchan_rel(struct vmbus_channel **subchan, int subchan_cnt __unused) { free(subchan, M_TEMP); } void vmbus_subchan_drain(struct vmbus_channel *pri_chan) { mtx_lock(&pri_chan->ch_subchan_lock); while (pri_chan->ch_subchan_cnt > 0) mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "dsubch", 0); mtx_unlock(&pri_chan->ch_subchan_lock); } void vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg) { vmbus_chanmsg_proc_t msg_proc; uint32_t msg_type; msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type; KASSERT(msg_type < VMBUS_CHANMSG_TYPE_MAX, ("invalid message type %u", msg_type)); msg_proc = vmbus_chan_msgprocs[msg_type]; if (msg_proc != NULL) msg_proc(sc, msg); } void vmbus_chan_set_readbatch(struct vmbus_channel *chan, bool on) { if (!on) chan->ch_flags &= ~VMBUS_CHAN_FLAG_BATCHREAD; else chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD; } uint32_t vmbus_chan_id(const struct vmbus_channel *chan) { return chan->ch_id; } uint32_t vmbus_chan_subidx(const struct vmbus_channel *chan) { return chan->ch_subidx; } bool vmbus_chan_is_primary(const struct vmbus_channel *chan) { if (VMBUS_CHAN_ISPRIMARY(chan)) return true; else return false; } const struct hyperv_guid * vmbus_chan_guid_inst(const struct vmbus_channel *chan) { return &chan->ch_guid_inst; } int vmbus_chan_prplist_nelem(int br_size, int prpcnt_max, int dlen_max) { int elem_size; elem_size = __offsetof(struct vmbus_chanpkt_prplist, cp_range[0].gpa_page[prpcnt_max]); elem_size += dlen_max; elem_size = VMBUS_CHANPKT_TOTLEN(elem_size); return (vmbus_br_nelem(br_size, elem_size)); } Index: projects/clang390-import/sys/dev/hyperv/vmbus/vmbus_reg.h =================================================================== --- projects/clang390-import/sys/dev/hyperv/vmbus/vmbus_reg.h (revision 305430) +++ projects/clang390-import/sys/dev/hyperv/vmbus/vmbus_reg.h (revision 305431) @@ -1,333 +1,336 @@ /*- * Copyright (c) 2016 Microsoft Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _VMBUS_REG_H_ #define _VMBUS_REG_H_ #include #include /* XXX for hyperv_guid */ #include #include /* * Hyper-V SynIC message format. */ #define VMBUS_MSG_DSIZE_MAX 240 #define VMBUS_MSG_SIZE 256 struct vmbus_message { uint32_t msg_type; /* HYPERV_MSGTYPE_ */ uint8_t msg_dsize; /* data size */ uint8_t msg_flags; /* VMBUS_MSGFLAG_ */ uint16_t msg_rsvd; uint64_t msg_id; uint8_t msg_data[VMBUS_MSG_DSIZE_MAX]; } __packed; CTASSERT(sizeof(struct vmbus_message) == VMBUS_MSG_SIZE); #define VMBUS_MSGFLAG_PENDING 0x01 /* * Hyper-V SynIC event flags */ #ifdef __LP64__ #define VMBUS_EVTFLAGS_MAX 32 #define VMBUS_EVTFLAG_SHIFT 6 #else #define VMBUS_EVTFLAGS_MAX 64 #define VMBUS_EVTFLAG_SHIFT 5 #endif #define VMBUS_EVTFLAG_LEN (1 << VMBUS_EVTFLAG_SHIFT) #define VMBUS_EVTFLAG_MASK (VMBUS_EVTFLAG_LEN - 1) #define VMBUS_EVTFLAGS_SIZE 256 struct vmbus_evtflags { u_long evt_flags[VMBUS_EVTFLAGS_MAX]; } __packed; CTASSERT(sizeof(struct vmbus_evtflags) == VMBUS_EVTFLAGS_SIZE); /* * Hyper-V Monitor Notification Facility */ struct vmbus_mon_trig { uint32_t mt_pending; uint32_t mt_armed; } __packed; #define VMBUS_MONTRIGS_MAX 4 #define VMBUS_MONTRIG_LEN 32 struct vmbus_mnf { uint32_t mnf_state; uint32_t mnf_rsvd1; struct vmbus_mon_trig mnf_trigs[VMBUS_MONTRIGS_MAX]; uint8_t mnf_rsvd2[536]; uint16_t mnf_lat[VMBUS_MONTRIGS_MAX][VMBUS_MONTRIG_LEN]; uint8_t mnf_rsvd3[256]; struct hyperv_mon_param mnf_param[VMBUS_MONTRIGS_MAX][VMBUS_MONTRIG_LEN]; uint8_t mnf_rsvd4[1984]; } __packed; CTASSERT(sizeof(struct vmbus_mnf) == PAGE_SIZE); /* * Buffer ring */ struct vmbus_bufring { /* * If br_windex == br_rindex, this bufring is empty; this * means we can _not_ write data to the bufring, if the * write is going to make br_windex same as br_rindex. */ volatile uint32_t br_windex; volatile uint32_t br_rindex; /* * Interrupt mask {0,1} * * For TX bufring, host set this to 1, when it is processing * the TX bufring, so that we can safely skip the TX event * notification to host. * * For RX bufring, once this is set to 1 by us, host will not * further dispatch interrupts to us, even if there are data * pending on the RX bufring. This effectively disables the * interrupt of the channel to which this RX bufring is attached. */ volatile uint32_t br_imask; uint8_t br_rsvd[4084]; uint8_t br_data[]; } __packed; CTASSERT(sizeof(struct vmbus_bufring) == PAGE_SIZE); /* * Channel */ #define VMBUS_CHAN_MAX_COMPAT 256 #define VMBUS_CHAN_MAX (VMBUS_EVTFLAG_LEN * VMBUS_EVTFLAGS_MAX) /* * Channel packets */ #define VMBUS_CHANPKT_SIZE_ALIGN (1 << VMBUS_CHANPKT_SIZE_SHIFT) #define VMBUS_CHANPKT_SETLEN(pktlen, len) \ do { \ (pktlen) = (len) >> VMBUS_CHANPKT_SIZE_SHIFT; \ } while (0) #define VMBUS_CHANPKT_TOTLEN(tlen) \ roundup2((tlen), VMBUS_CHANPKT_SIZE_ALIGN) +#define VMBUS_CHANPKT_HLEN_MIN \ + (sizeof(struct vmbus_chanpkt_hdr) >> VMBUS_CHANPKT_SIZE_SHIFT) + struct vmbus_chanpkt { struct vmbus_chanpkt_hdr cp_hdr; } __packed; struct vmbus_chanpkt_sglist { struct vmbus_chanpkt_hdr cp_hdr; uint32_t cp_rsvd; uint32_t cp_gpa_cnt; struct vmbus_gpa cp_gpa[]; } __packed; struct vmbus_chanpkt_prplist { struct vmbus_chanpkt_hdr cp_hdr; uint32_t cp_rsvd; uint32_t cp_range_cnt; struct vmbus_gpa_range cp_range[]; } __packed; /* * Channel messages * - Embedded in vmbus_message.msg_data, e.g. response and notification. * - Embedded in hypercall_postmsg_in.hc_data, e.g. request. */ #define VMBUS_CHANMSG_TYPE_CHOFFER 1 /* NOTE */ #define VMBUS_CHANMSG_TYPE_CHRESCIND 2 /* NOTE */ #define VMBUS_CHANMSG_TYPE_CHREQUEST 3 /* REQ */ #define VMBUS_CHANMSG_TYPE_CHOFFER_DONE 4 /* NOTE */ #define VMBUS_CHANMSG_TYPE_CHOPEN 5 /* REQ */ #define VMBUS_CHANMSG_TYPE_CHOPEN_RESP 6 /* RESP */ #define VMBUS_CHANMSG_TYPE_CHCLOSE 7 /* REQ */ #define VMBUS_CHANMSG_TYPE_GPADL_CONN 8 /* REQ */ #define VMBUS_CHANMSG_TYPE_GPADL_SUBCONN 9 /* REQ */ #define VMBUS_CHANMSG_TYPE_GPADL_CONNRESP 10 /* RESP */ #define VMBUS_CHANMSG_TYPE_GPADL_DISCONN 11 /* REQ */ #define VMBUS_CHANMSG_TYPE_GPADL_DISCONNRESP 12 /* RESP */ #define VMBUS_CHANMSG_TYPE_CHFREE 13 /* REQ */ #define VMBUS_CHANMSG_TYPE_CONNECT 14 /* REQ */ #define VMBUS_CHANMSG_TYPE_CONNECT_RESP 15 /* RESP */ #define VMBUS_CHANMSG_TYPE_DISCONNECT 16 /* REQ */ #define VMBUS_CHANMSG_TYPE_MAX 22 struct vmbus_chanmsg_hdr { uint32_t chm_type; /* VMBUS_CHANMSG_TYPE_ */ uint32_t chm_rsvd; } __packed; /* VMBUS_CHANMSG_TYPE_CONNECT */ struct vmbus_chanmsg_connect { struct vmbus_chanmsg_hdr chm_hdr; uint32_t chm_ver; uint32_t chm_rsvd; uint64_t chm_evtflags; uint64_t chm_mnf1; uint64_t chm_mnf2; } __packed; /* VMBUS_CHANMSG_TYPE_CONNECT_RESP */ struct vmbus_chanmsg_connect_resp { struct vmbus_chanmsg_hdr chm_hdr; uint8_t chm_done; } __packed; /* VMBUS_CHANMSG_TYPE_CHREQUEST */ struct vmbus_chanmsg_chrequest { struct vmbus_chanmsg_hdr chm_hdr; } __packed; /* VMBUS_CHANMSG_TYPE_DISCONNECT */ struct vmbus_chanmsg_disconnect { struct vmbus_chanmsg_hdr chm_hdr; } __packed; /* VMBUS_CHANMSG_TYPE_CHOPEN */ struct vmbus_chanmsg_chopen { struct vmbus_chanmsg_hdr chm_hdr; uint32_t chm_chanid; uint32_t chm_openid; uint32_t chm_gpadl; uint32_t chm_vcpuid; uint32_t chm_txbr_pgcnt; #define VMBUS_CHANMSG_CHOPEN_UDATA_SIZE 120 uint8_t chm_udata[VMBUS_CHANMSG_CHOPEN_UDATA_SIZE]; } __packed; /* VMBUS_CHANMSG_TYPE_CHOPEN_RESP */ struct vmbus_chanmsg_chopen_resp { struct vmbus_chanmsg_hdr chm_hdr; uint32_t chm_chanid; uint32_t chm_openid; uint32_t chm_status; } __packed; /* VMBUS_CHANMSG_TYPE_GPADL_CONN */ struct vmbus_chanmsg_gpadl_conn { struct vmbus_chanmsg_hdr chm_hdr; uint32_t chm_chanid; uint32_t chm_gpadl; uint16_t chm_range_len; uint16_t chm_range_cnt; struct vmbus_gpa_range chm_range; } __packed; #define VMBUS_CHANMSG_GPADL_CONN_PGMAX 26 CTASSERT(__offsetof(struct vmbus_chanmsg_gpadl_conn, chm_range.gpa_page[VMBUS_CHANMSG_GPADL_CONN_PGMAX]) <= HYPERCALL_POSTMSGIN_DSIZE_MAX); /* VMBUS_CHANMSG_TYPE_GPADL_SUBCONN */ struct vmbus_chanmsg_gpadl_subconn { struct vmbus_chanmsg_hdr chm_hdr; uint32_t chm_msgno; uint32_t chm_gpadl; uint64_t chm_gpa_page[]; } __packed; #define VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX 28 CTASSERT(__offsetof(struct vmbus_chanmsg_gpadl_subconn, chm_gpa_page[VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX]) <= HYPERCALL_POSTMSGIN_DSIZE_MAX); /* VMBUS_CHANMSG_TYPE_GPADL_CONNRESP */ struct vmbus_chanmsg_gpadl_connresp { struct vmbus_chanmsg_hdr chm_hdr; uint32_t chm_chanid; uint32_t chm_gpadl; uint32_t chm_status; } __packed; /* VMBUS_CHANMSG_TYPE_CHCLOSE */ struct vmbus_chanmsg_chclose { struct vmbus_chanmsg_hdr chm_hdr; uint32_t chm_chanid; } __packed; /* VMBUS_CHANMSG_TYPE_GPADL_DISCONN */ struct vmbus_chanmsg_gpadl_disconn { struct vmbus_chanmsg_hdr chm_hdr; uint32_t chm_chanid; uint32_t chm_gpadl; } __packed; /* VMBUS_CHANMSG_TYPE_CHFREE */ struct vmbus_chanmsg_chfree { struct vmbus_chanmsg_hdr chm_hdr; uint32_t chm_chanid; } __packed; /* VMBUS_CHANMSG_TYPE_CHRESCIND */ struct vmbus_chanmsg_chrescind { struct vmbus_chanmsg_hdr chm_hdr; uint32_t chm_chanid; } __packed; /* VMBUS_CHANMSG_TYPE_CHOFFER */ struct vmbus_chanmsg_choffer { struct vmbus_chanmsg_hdr chm_hdr; struct hyperv_guid chm_chtype; struct hyperv_guid chm_chinst; uint64_t chm_chlat; /* unit: 100ns */ uint32_t chm_chrev; uint32_t chm_svrctx_sz; uint16_t chm_chflags; uint16_t chm_mmio_sz; /* unit: MB */ uint8_t chm_udata[120]; uint16_t chm_subidx; uint16_t chm_rsvd; uint32_t chm_chanid; uint8_t chm_montrig; uint8_t chm_flags1; /* VMBUS_CHOFFER_FLAG1_ */ uint16_t chm_flags2; uint32_t chm_connid; } __packed; CTASSERT(sizeof(struct vmbus_chanmsg_choffer) <= VMBUS_MSG_DSIZE_MAX); #define VMBUS_CHOFFER_FLAG1_HASMNF 0x01 #endif /* !_VMBUS_REG_H_ */ Index: projects/clang390-import/sys/dev/iicbus/sy8106a.c =================================================================== --- projects/clang390-import/sys/dev/iicbus/sy8106a.c (nonexistent) +++ projects/clang390-import/sys/dev/iicbus/sy8106a.c (revision 305431) @@ -0,0 +1,302 @@ +/*- + * Copyright (c) 2016 Jared McNeill + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Silergy Corp. SY8106A buck regulator + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include "iicbus_if.h" +#include "regdev_if.h" + +#define VOUT1_SEL 0x01 +#define SEL_GO (1 << 7) +#define SEL_VOLTAGE_MASK 0x7f +#define SEL_VOLTAGE_BASE 680000 /* uV */ +#define SEL_VOLTAGE_STEP 10000 /* uV */ +#define VOUT_COM 0x02 +#define COM_DISABLE (1 << 0) +#define SYS_STATUS 0x06 + +static struct ofw_compat_data compat_data[] = { + { "silergy,sy8106a", 1 }, + { NULL, 0 } +}; + +struct sy8106a_reg_sc { + struct regnode *regnode; + device_t base_dev; + phandle_t xref; + struct regnode_std_param *param; +}; + +struct sy8106a_softc { + uint16_t addr; + + /* Regulator */ + struct sy8106a_reg_sc *reg; +}; + +static int +sy8106a_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) +{ + struct sy8106a_softc *sc; + struct iic_msg msg[2]; + + sc = device_get_softc(dev); + + msg[0].slave = sc->addr; + msg[0].flags = IIC_M_WR; + msg[0].len = 1; + msg[0].buf = ® + + msg[1].slave = sc->addr; + msg[1].flags = IIC_M_RD; + msg[1].len = size; + msg[1].buf = data; + + return (iicbus_transfer(dev, msg, 2)); +} + +static int +sy8106a_write(device_t dev, uint8_t reg, uint8_t val) +{ + struct sy8106a_softc *sc; + struct iic_msg msg; + uint8_t buffer[2]; + + sc = device_get_softc(dev); + + buffer[0] = reg; + buffer[1] = val; + + msg.slave = sc->addr; + msg.flags = IIC_M_WR; + msg.len = 2; + msg.buf = buffer; + + return (iicbus_transfer(dev, &msg, 1)); +} + +static int +sy8106a_regnode_init(struct regnode *regnode) +{ + return (0); +} + +static int +sy8106a_regnode_enable(struct regnode *regnode, bool enable, int *udelay) +{ + struct sy8106a_reg_sc *sc; + uint8_t val; + + sc = regnode_get_softc(regnode); + + sy8106a_read(sc->base_dev, VOUT_COM, &val, 1); + if (enable) + val &= ~COM_DISABLE; + else + val |= COM_DISABLE; + sy8106a_write(sc->base_dev, VOUT_COM, val); + + *udelay = sc->param->ramp_delay; + + return (0); +} + +static int +sy8106a_regnode_set_voltage(struct regnode *regnode, int min_uvolt, + int max_uvolt, int *udelay) +{ + struct sy8106a_reg_sc *sc; + int cur_uvolt; + uint8_t val, oval; + + sc = regnode_get_softc(regnode); + + /* Get current voltage */ + sy8106a_read(sc->base_dev, VOUT1_SEL, &oval, 1); + cur_uvolt = (oval & SEL_VOLTAGE_MASK) * SEL_VOLTAGE_STEP + + SEL_VOLTAGE_BASE; + + /* Set new voltage */ + val = SEL_GO | ((min_uvolt - SEL_VOLTAGE_BASE) / SEL_VOLTAGE_STEP); + sy8106a_write(sc->base_dev, VOUT1_SEL, val); + + /* Time to delay is based on the number of voltage steps */ + *udelay = sc->param->ramp_delay * + (abs(cur_uvolt - min_uvolt) / SEL_VOLTAGE_STEP); + + return (0); +} + +static int +sy8106a_regnode_get_voltage(struct regnode *regnode, int *uvolt) +{ + struct sy8106a_reg_sc *sc; + uint8_t val; + + sc = regnode_get_softc(regnode); + + sy8106a_read(sc->base_dev, VOUT1_SEL, &val, 1); + *uvolt = (val & SEL_VOLTAGE_MASK) * SEL_VOLTAGE_STEP + + SEL_VOLTAGE_BASE; + + return (0); +} + +static regnode_method_t sy8106a_regnode_methods[] = { + /* Regulator interface */ + REGNODEMETHOD(regnode_init, sy8106a_regnode_init), + REGNODEMETHOD(regnode_enable, sy8106a_regnode_enable), + REGNODEMETHOD(regnode_set_voltage, sy8106a_regnode_set_voltage), + REGNODEMETHOD(regnode_get_voltage, sy8106a_regnode_get_voltage), + REGNODEMETHOD_END +}; +DEFINE_CLASS_1(sy8106a_regnode, sy8106a_regnode_class, sy8106a_regnode_methods, + sizeof(struct sy8106a_reg_sc), regnode_class); + +static struct sy8106a_reg_sc * +sy8106a_reg_attach(device_t dev, phandle_t node) +{ + struct sy8106a_reg_sc *reg_sc; + struct regnode_init_def initdef; + struct regnode *regnode; + + memset(&initdef, 0, sizeof(initdef)); + regulator_parse_ofw_stdparam(dev, node, &initdef); + initdef.id = 0; + initdef.ofw_node = node; + regnode = regnode_create(dev, &sy8106a_regnode_class, &initdef); + if (regnode == NULL) { + device_printf(dev, "cannot create regulator\n"); + return (NULL); + } + + reg_sc = regnode_get_softc(regnode); + reg_sc->regnode = regnode; + reg_sc->base_dev = dev; + reg_sc->xref = OF_xref_from_node(node); + reg_sc->param = regnode_get_stdparam(regnode); + + regnode_register(regnode); + + return (reg_sc); +} + +static int +sy8106a_regdev_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, + intptr_t *num) +{ + struct sy8106a_softc *sc; + + sc = device_get_softc(dev); + + if (sc->reg->xref != xref) + return (ENXIO); + + *num = 0; + + return (0); +} + +static int +sy8106a_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "Silergy SY8106A regulator"); + + return (BUS_PROBE_DEFAULT); +} + +static int +sy8106a_attach(device_t dev) +{ + struct sy8106a_softc *sc; + phandle_t node; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + + sc->addr = iicbus_get_addr(dev); + + sc->reg = sy8106a_reg_attach(dev, node); + if (sc->reg == NULL) { + device_printf(dev, "cannot attach regulator\n"); + return (ENXIO); + } + + return (0); +} + +static device_method_t sy8106a_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sy8106a_probe), + DEVMETHOD(device_attach, sy8106a_attach), + + /* Regdev interface */ + DEVMETHOD(regdev_map, sy8106a_regdev_map), + + DEVMETHOD_END +}; + +static driver_t sy8106a_driver = { + "sy8106a", + sy8106a_methods, + sizeof(struct sy8106a_softc), +}; + +static devclass_t sy8106a_devclass; + +EARLY_DRIVER_MODULE(sy8106a, iicbus, sy8106a_driver, sy8106a_devclass, 0, 0, + BUS_PASS_RESOURCE); +MODULE_VERSION(sy8106a, 1); +MODULE_DEPEND(sy8106a, iicbus, 1, 1, 1); Property changes on: projects/clang390-import/sys/dev/iicbus/sy8106a.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang390-import/sys/dev/usb/template/usb_template.c =================================================================== --- projects/clang390-import/sys/dev/usb/template/usb_template.c (revision 305430) +++ projects/clang390-import/sys/dev/usb/template/usb_template.c (revision 305431) @@ -1,1397 +1,1397 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2007 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This file contains sub-routines to build up USB descriptors from * USB templates. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #include #include #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ MODULE_DEPEND(usb_template, usb, 1, 1, 1); MODULE_VERSION(usb_template, 1); /* function prototypes */ static void usb_make_raw_desc(struct usb_temp_setup *, const uint8_t *); static void usb_make_endpoint_desc(struct usb_temp_setup *, const struct usb_temp_endpoint_desc *); static void usb_make_interface_desc(struct usb_temp_setup *, const struct usb_temp_interface_desc *); static void usb_make_config_desc(struct usb_temp_setup *, const struct usb_temp_config_desc *); static void usb_make_device_desc(struct usb_temp_setup *, const struct usb_temp_device_desc *); static uint8_t usb_hw_ep_match(const struct usb_hw_ep_profile *, uint8_t, uint8_t); static uint8_t usb_hw_ep_find_match(struct usb_hw_ep_scratch *, struct usb_hw_ep_scratch_sub *, uint8_t); static uint8_t usb_hw_ep_get_needs(struct usb_hw_ep_scratch *, uint8_t, uint8_t); static usb_error_t usb_hw_ep_resolve(struct usb_device *, struct usb_descriptor *); static const struct usb_temp_device_desc *usb_temp_get_tdd(struct usb_device *); static void *usb_temp_get_device_desc(struct usb_device *); static void *usb_temp_get_qualifier_desc(struct usb_device *); static void *usb_temp_get_config_desc(struct usb_device *, uint16_t *, uint8_t); static const void *usb_temp_get_string_desc(struct usb_device *, uint16_t, uint8_t); static const void *usb_temp_get_vendor_desc(struct usb_device *, const struct usb_device_request *, uint16_t *plen); static const void *usb_temp_get_hub_desc(struct usb_device *); static usb_error_t usb_temp_get_desc(struct usb_device *, struct usb_device_request *, const void **, uint16_t *); static usb_error_t usb_temp_setup_by_index(struct usb_device *, uint16_t index); static void usb_temp_init(void *); /*------------------------------------------------------------------------* * usb_make_raw_desc * * This function will insert a raw USB descriptor into the generated * USB configuration. *------------------------------------------------------------------------*/ static void usb_make_raw_desc(struct usb_temp_setup *temp, const uint8_t *raw) { void *dst; uint8_t len; /* * The first byte of any USB descriptor gives the length. */ if (raw) { len = raw[0]; if (temp->buf) { dst = USB_ADD_BYTES(temp->buf, temp->size); memcpy(dst, raw, len); /* check if we have got a CDC union descriptor */ if ((raw[0] == sizeof(struct usb_cdc_union_descriptor)) && (raw[1] == UDESC_CS_INTERFACE) && (raw[2] == UDESCSUB_CDC_UNION)) { struct usb_cdc_union_descriptor *ud = (void *)dst; /* update the interface numbers */ ud->bMasterInterface += temp->bInterfaceNumber; ud->bSlaveInterface[0] += temp->bInterfaceNumber; } /* check if we have got an interface association descriptor */ if ((raw[0] == sizeof(struct usb_interface_assoc_descriptor)) && (raw[1] == UDESC_IFACE_ASSOC)) { struct usb_interface_assoc_descriptor *iad = (void *)dst; /* update the interface number */ iad->bFirstInterface += temp->bInterfaceNumber; } /* check if we have got a call management descriptor */ if ((raw[0] == sizeof(struct usb_cdc_cm_descriptor)) && (raw[1] == UDESC_CS_INTERFACE) && (raw[2] == UDESCSUB_CDC_CM)) { struct usb_cdc_cm_descriptor *ccd = (void *)dst; /* update the interface number */ ccd->bDataInterface += temp->bInterfaceNumber; } } temp->size += len; } } /*------------------------------------------------------------------------* * usb_make_endpoint_desc * * This function will generate an USB endpoint descriptor from the * given USB template endpoint descriptor, which will be inserted into * the USB configuration. *------------------------------------------------------------------------*/ static void usb_make_endpoint_desc(struct usb_temp_setup *temp, const struct usb_temp_endpoint_desc *ted) { struct usb_endpoint_descriptor *ed; const void **rd; uint16_t old_size; uint16_t mps; uint8_t ea; /* Endpoint Address */ uint8_t et; /* Endpiont Type */ /* Reserve memory */ old_size = temp->size; ea = (ted->bEndpointAddress & (UE_ADDR | UE_DIR_IN | UE_DIR_OUT)); et = (ted->bmAttributes & UE_XFERTYPE); if (et == UE_ISOCHRONOUS) { /* account for extra byte fields */ temp->size += sizeof(*ed) + 2; } else { temp->size += sizeof(*ed); } /* Scan all Raw Descriptors first */ rd = ted->ppRawDesc; if (rd) { while (*rd) { usb_make_raw_desc(temp, *rd); rd++; } } if (ted->pPacketSize == NULL) { /* not initialized */ temp->err = USB_ERR_INVAL; return; } mps = ted->pPacketSize->mps[temp->usb_speed]; if (mps == 0) { /* not initialized */ temp->err = USB_ERR_INVAL; return; } else if (mps == UE_ZERO_MPS) { /* escape for Zero Max Packet Size */ mps = 0; } /* * Fill out the real USB endpoint descriptor * in case there is a buffer present: */ if (temp->buf) { ed = USB_ADD_BYTES(temp->buf, old_size); if (et == UE_ISOCHRONOUS) ed->bLength = sizeof(*ed) + 2; else ed->bLength = sizeof(*ed); ed->bDescriptorType = UDESC_ENDPOINT; ed->bEndpointAddress = ea; ed->bmAttributes = ted->bmAttributes; USETW(ed->wMaxPacketSize, mps); /* setup bInterval parameter */ if (ted->pIntervals && ted->pIntervals->bInterval[temp->usb_speed]) { ed->bInterval = ted->pIntervals->bInterval[temp->usb_speed]; } else { switch (et) { case UE_BULK: case UE_CONTROL: ed->bInterval = 0; /* not used */ break; case UE_INTERRUPT: switch (temp->usb_speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: ed->bInterval = 1; /* 1 ms */ break; default: ed->bInterval = 4; /* 1 ms */ break; } break; default: /* UE_ISOCHRONOUS */ switch (temp->usb_speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: ed->bInterval = 1; /* 1 ms */ break; default: ed->bInterval = 1; /* 125 us */ break; } break; } } } temp->bNumEndpoints++; } /*------------------------------------------------------------------------* * usb_make_interface_desc * * This function will generate an USB interface descriptor from the * given USB template interface descriptor, which will be inserted * into the USB configuration. *------------------------------------------------------------------------*/ static void usb_make_interface_desc(struct usb_temp_setup *temp, const struct usb_temp_interface_desc *tid) { struct usb_interface_descriptor *id; const struct usb_temp_endpoint_desc **ted; const void **rd; uint16_t old_size; /* Reserve memory */ old_size = temp->size; temp->size += sizeof(*id); /* Update interface and alternate interface numbers */ if (tid->isAltInterface == 0) { temp->bAlternateSetting = 0; temp->bInterfaceNumber++; } else { temp->bAlternateSetting++; } /* Scan all Raw Descriptors first */ rd = tid->ppRawDesc; if (rd) { while (*rd) { usb_make_raw_desc(temp, *rd); rd++; } } /* Reset some counters */ temp->bNumEndpoints = 0; /* Scan all Endpoint Descriptors second */ ted = tid->ppEndpoints; if (ted) { while (*ted) { usb_make_endpoint_desc(temp, *ted); ted++; } } /* * Fill out the real USB interface descriptor * in case there is a buffer present: */ if (temp->buf) { id = USB_ADD_BYTES(temp->buf, old_size); id->bLength = sizeof(*id); id->bDescriptorType = UDESC_INTERFACE; id->bInterfaceNumber = temp->bInterfaceNumber; id->bAlternateSetting = temp->bAlternateSetting; id->bNumEndpoints = temp->bNumEndpoints; id->bInterfaceClass = tid->bInterfaceClass; id->bInterfaceSubClass = tid->bInterfaceSubClass; id->bInterfaceProtocol = tid->bInterfaceProtocol; id->iInterface = tid->iInterface; } } /*------------------------------------------------------------------------* * usb_make_config_desc * * This function will generate an USB config descriptor from the given * USB template config descriptor, which will be inserted into the USB * configuration. *------------------------------------------------------------------------*/ static void usb_make_config_desc(struct usb_temp_setup *temp, const struct usb_temp_config_desc *tcd) { struct usb_config_descriptor *cd; const struct usb_temp_interface_desc **tid; uint16_t old_size; /* Reserve memory */ old_size = temp->size; temp->size += sizeof(*cd); /* Reset some counters */ temp->bInterfaceNumber = 0xFF; temp->bAlternateSetting = 0; /* Scan all the USB interfaces */ tid = tcd->ppIfaceDesc; if (tid) { while (*tid) { usb_make_interface_desc(temp, *tid); tid++; } } /* * Fill out the real USB config descriptor * in case there is a buffer present: */ if (temp->buf) { cd = USB_ADD_BYTES(temp->buf, old_size); /* compute total size */ old_size = temp->size - old_size; cd->bLength = sizeof(*cd); cd->bDescriptorType = UDESC_CONFIG; USETW(cd->wTotalLength, old_size); cd->bNumInterface = temp->bInterfaceNumber + 1; cd->bConfigurationValue = temp->bConfigurationValue; cd->iConfiguration = tcd->iConfiguration; cd->bmAttributes = tcd->bmAttributes; cd->bMaxPower = tcd->bMaxPower; cd->bmAttributes |= (UC_REMOTE_WAKEUP | UC_BUS_POWERED); if (temp->self_powered) { cd->bmAttributes |= UC_SELF_POWERED; } else { cd->bmAttributes &= ~UC_SELF_POWERED; } } } /*------------------------------------------------------------------------* * usb_make_device_desc * * This function will generate an USB device descriptor from the * given USB template device descriptor. *------------------------------------------------------------------------*/ static void usb_make_device_desc(struct usb_temp_setup *temp, const struct usb_temp_device_desc *tdd) { struct usb_temp_data *utd; const struct usb_temp_config_desc **tcd; uint16_t old_size; /* Reserve memory */ old_size = temp->size; temp->size += sizeof(*utd); /* Scan all the USB configs */ temp->bConfigurationValue = 1; tcd = tdd->ppConfigDesc; if (tcd) { while (*tcd) { usb_make_config_desc(temp, *tcd); temp->bConfigurationValue++; tcd++; } } /* * Fill out the real USB device descriptor * in case there is a buffer present: */ if (temp->buf) { utd = USB_ADD_BYTES(temp->buf, old_size); /* Store a pointer to our template device descriptor */ utd->tdd = tdd; /* Fill out USB device descriptor */ utd->udd.bLength = sizeof(utd->udd); utd->udd.bDescriptorType = UDESC_DEVICE; utd->udd.bDeviceClass = tdd->bDeviceClass; utd->udd.bDeviceSubClass = tdd->bDeviceSubClass; utd->udd.bDeviceProtocol = tdd->bDeviceProtocol; USETW(utd->udd.idVendor, tdd->idVendor); USETW(utd->udd.idProduct, tdd->idProduct); USETW(utd->udd.bcdDevice, tdd->bcdDevice); utd->udd.iManufacturer = tdd->iManufacturer; utd->udd.iProduct = tdd->iProduct; utd->udd.iSerialNumber = tdd->iSerialNumber; utd->udd.bNumConfigurations = temp->bConfigurationValue - 1; /* * Fill out the USB device qualifier. Pretend that we * don't support any other speeds by setting * "bNumConfigurations" equal to zero. That saves us * generating an extra set of configuration * descriptors. */ utd->udq.bLength = sizeof(utd->udq); utd->udq.bDescriptorType = UDESC_DEVICE_QUALIFIER; utd->udq.bDeviceClass = tdd->bDeviceClass; utd->udq.bDeviceSubClass = tdd->bDeviceSubClass; utd->udq.bDeviceProtocol = tdd->bDeviceProtocol; utd->udq.bNumConfigurations = 0; USETW(utd->udq.bcdUSB, 0x0200); utd->udq.bMaxPacketSize0 = 0; switch (temp->usb_speed) { case USB_SPEED_LOW: USETW(utd->udd.bcdUSB, 0x0110); utd->udd.bMaxPacketSize = 8; break; case USB_SPEED_FULL: USETW(utd->udd.bcdUSB, 0x0110); utd->udd.bMaxPacketSize = 32; break; case USB_SPEED_HIGH: USETW(utd->udd.bcdUSB, 0x0200); utd->udd.bMaxPacketSize = 64; break; case USB_SPEED_VARIABLE: USETW(utd->udd.bcdUSB, 0x0250); utd->udd.bMaxPacketSize = 255; /* 512 bytes */ break; case USB_SPEED_SUPER: USETW(utd->udd.bcdUSB, 0x0300); utd->udd.bMaxPacketSize = 9; /* 2**9 = 512 bytes */ break; default: temp->err = USB_ERR_INVAL; break; } } } /*------------------------------------------------------------------------* * usb_hw_ep_match * * Return values: * 0: The endpoint profile does not match the criteria * Else: The endpoint profile matches the criteria *------------------------------------------------------------------------*/ static uint8_t usb_hw_ep_match(const struct usb_hw_ep_profile *pf, uint8_t ep_type, uint8_t ep_dir_in) { if (ep_type == UE_CONTROL) { /* special */ return (pf->support_control); } if ((pf->support_in && ep_dir_in) || (pf->support_out && !ep_dir_in)) { if ((pf->support_interrupt && (ep_type == UE_INTERRUPT)) || (pf->support_isochronous && (ep_type == UE_ISOCHRONOUS)) || (pf->support_bulk && (ep_type == UE_BULK))) { return (1); } } return (0); } /*------------------------------------------------------------------------* * usb_hw_ep_find_match * * This function is used to find the best matching endpoint profile * for and endpoint belonging to an USB descriptor. * * Return values: * 0: Success. Got a match. * Else: Failure. No match. *------------------------------------------------------------------------*/ static uint8_t usb_hw_ep_find_match(struct usb_hw_ep_scratch *ues, struct usb_hw_ep_scratch_sub *ep, uint8_t is_simplex) { const struct usb_hw_ep_profile *pf; uint16_t distance; uint16_t temp; uint16_t max_frame_size; uint8_t n; uint8_t best_n; uint8_t dir_in; uint8_t dir_out; distance = 0xFFFF; best_n = 0; if ((!ep->needs_in) && (!ep->needs_out)) { return (0); /* we are done */ } if (ep->needs_ep_type == UE_CONTROL) { dir_in = 1; dir_out = 1; } else { if (ep->needs_in) { dir_in = 1; dir_out = 0; } else { dir_in = 0; dir_out = 1; } } for (n = 1; n != (USB_EP_MAX / 2); n++) { /* get HW endpoint profile */ (ues->methods->get_hw_ep_profile) (ues->udev, &pf, n); if (pf == NULL) { /* end of profiles */ break; } /* check if IN-endpoint is reserved */ if (dir_in || pf->is_simplex) { if (ues->bmInAlloc[n / 8] & (1 << (n % 8))) { /* mismatch */ continue; } } /* check if OUT-endpoint is reserved */ if (dir_out || pf->is_simplex) { if (ues->bmOutAlloc[n / 8] & (1 << (n % 8))) { /* mismatch */ continue; } } /* check simplex */ if (pf->is_simplex == is_simplex) { /* mismatch */ continue; } /* check if HW endpoint matches */ if (!usb_hw_ep_match(pf, ep->needs_ep_type, dir_in)) { /* mismatch */ continue; } /* get maximum frame size */ if (dir_in) max_frame_size = pf->max_in_frame_size; else max_frame_size = pf->max_out_frame_size; /* check if we have a matching profile */ if (max_frame_size >= ep->max_frame_size) { temp = (max_frame_size - ep->max_frame_size); if (distance > temp) { distance = temp; best_n = n; ep->pf = pf; } } } /* see if we got a match */ if (best_n != 0) { /* get the correct profile */ pf = ep->pf; /* reserve IN-endpoint */ if (dir_in) { ues->bmInAlloc[best_n / 8] |= (1 << (best_n % 8)); ep->hw_endpoint_in = best_n | UE_DIR_IN; ep->needs_in = 0; } /* reserve OUT-endpoint */ if (dir_out) { ues->bmOutAlloc[best_n / 8] |= (1 << (best_n % 8)); ep->hw_endpoint_out = best_n | UE_DIR_OUT; ep->needs_out = 0; } return (0); /* got a match */ } return (1); /* failure */ } /*------------------------------------------------------------------------* * usb_hw_ep_get_needs * * This function will figure out the type and number of endpoints * which are needed for an USB configuration. * * Return values: * 0: Success. * Else: Failure. *------------------------------------------------------------------------*/ static uint8_t usb_hw_ep_get_needs(struct usb_hw_ep_scratch *ues, uint8_t ep_type, uint8_t is_complete) { const struct usb_hw_ep_profile *pf; struct usb_hw_ep_scratch_sub *ep_iface; struct usb_hw_ep_scratch_sub *ep_curr; struct usb_hw_ep_scratch_sub *ep_max; struct usb_hw_ep_scratch_sub *ep_end; struct usb_descriptor *desc; struct usb_interface_descriptor *id; struct usb_endpoint_descriptor *ed; enum usb_dev_speed speed; uint16_t wMaxPacketSize; uint16_t temp; uint8_t ep_no; ep_iface = ues->ep_max; ep_curr = ues->ep_max; ep_end = ues->ep + USB_EP_MAX; ep_max = ues->ep_max; desc = NULL; speed = usbd_get_speed(ues->udev); repeat: while ((desc = usb_desc_foreach(ues->cd, desc))) { if ((desc->bDescriptorType == UDESC_INTERFACE) && (desc->bLength >= sizeof(*id))) { id = (void *)desc; if (id->bAlternateSetting == 0) { /* going forward */ ep_iface = ep_max; } else { /* reset */ ep_curr = ep_iface; } } if ((desc->bDescriptorType == UDESC_ENDPOINT) && (desc->bLength >= sizeof(*ed))) { ed = (void *)desc; goto handle_endpoint_desc; } } ues->ep_max = ep_max; return (0); handle_endpoint_desc: temp = (ed->bmAttributes & UE_XFERTYPE); if (temp == ep_type) { if (ep_curr == ep_end) { /* too many endpoints */ return (1); /* failure */ } wMaxPacketSize = UGETW(ed->wMaxPacketSize); if ((wMaxPacketSize & 0xF800) && (speed == USB_SPEED_HIGH)) { /* handle packet multiplier */ temp = (wMaxPacketSize >> 11) & 3; wMaxPacketSize &= 0x7FF; if (temp == 1) { wMaxPacketSize *= 2; } else { wMaxPacketSize *= 3; } } /* * Check if we have a fixed endpoint number, else the * endpoint number is allocated dynamically: */ ep_no = (ed->bEndpointAddress & UE_ADDR); if (ep_no != 0) { /* get HW endpoint profile */ (ues->methods->get_hw_ep_profile) (ues->udev, &pf, ep_no); if (pf == NULL) { /* HW profile does not exist - failure */ DPRINTFN(0, "Endpoint profile %u " "does not exist\n", ep_no); return (1); } /* reserve fixed endpoint number */ if (ep_type == UE_CONTROL) { ues->bmInAlloc[ep_no / 8] |= (1 << (ep_no % 8)); ues->bmOutAlloc[ep_no / 8] |= (1 << (ep_no % 8)); if ((pf->max_in_frame_size < wMaxPacketSize) || (pf->max_out_frame_size < wMaxPacketSize)) { DPRINTFN(0, "Endpoint profile %u " "has too small buffer\n", ep_no); return (1); } } else if (ed->bEndpointAddress & UE_DIR_IN) { ues->bmInAlloc[ep_no / 8] |= (1 << (ep_no % 8)); if (pf->max_in_frame_size < wMaxPacketSize) { DPRINTFN(0, "Endpoint profile %u " "has too small buffer\n", ep_no); return (1); } } else { ues->bmOutAlloc[ep_no / 8] |= (1 << (ep_no % 8)); if (pf->max_out_frame_size < wMaxPacketSize) { DPRINTFN(0, "Endpoint profile %u " "has too small buffer\n", ep_no); return (1); } } } else if (is_complete) { /* check if we have enough buffer space */ if (wMaxPacketSize > ep_curr->max_frame_size) { return (1); /* failure */ } if (ed->bEndpointAddress & UE_DIR_IN) { ed->bEndpointAddress = ep_curr->hw_endpoint_in; } else { ed->bEndpointAddress = ep_curr->hw_endpoint_out; } } else { /* compute the maximum frame size */ if (ep_curr->max_frame_size < wMaxPacketSize) { ep_curr->max_frame_size = wMaxPacketSize; } if (temp == UE_CONTROL) { ep_curr->needs_in = 1; ep_curr->needs_out = 1; } else { if (ed->bEndpointAddress & UE_DIR_IN) { ep_curr->needs_in = 1; } else { ep_curr->needs_out = 1; } } ep_curr->needs_ep_type = ep_type; } ep_curr++; if (ep_max < ep_curr) { ep_max = ep_curr; } } goto repeat; } /*------------------------------------------------------------------------* * usb_hw_ep_resolve * * This function will try to resolve endpoint requirements by the * given endpoint profiles that the USB hardware reports. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static usb_error_t usb_hw_ep_resolve(struct usb_device *udev, struct usb_descriptor *desc) { struct usb_hw_ep_scratch *ues; struct usb_hw_ep_scratch_sub *ep; const struct usb_hw_ep_profile *pf; const struct usb_bus_methods *methods; struct usb_device_descriptor *dd; uint16_t mps; if (desc == NULL) return (USB_ERR_INVAL); /* get bus methods */ methods = udev->bus->methods; if (methods->get_hw_ep_profile == NULL) return (USB_ERR_INVAL); if (desc->bDescriptorType == UDESC_DEVICE) { if (desc->bLength < sizeof(*dd)) return (USB_ERR_INVAL); dd = (void *)desc; /* get HW control endpoint 0 profile */ (methods->get_hw_ep_profile) (udev, &pf, 0); if (pf == NULL) { return (USB_ERR_INVAL); } if (!usb_hw_ep_match(pf, UE_CONTROL, 0)) { DPRINTFN(0, "Endpoint 0 does not " "support control\n"); return (USB_ERR_INVAL); } mps = dd->bMaxPacketSize; if (udev->speed == USB_SPEED_FULL) { /* * We can optionally choose another packet size ! */ while (1) { /* check if "mps" is ok */ if (pf->max_in_frame_size >= mps) { break; } /* reduce maximum packet size */ mps /= 2; /* check if "mps" is too small */ if (mps < 8) { return (USB_ERR_INVAL); } } dd->bMaxPacketSize = mps; } else { /* We only have one choice */ if (mps == 255) { mps = 512; } /* Check if we support the specified wMaxPacketSize */ if (pf->max_in_frame_size < mps) { return (USB_ERR_INVAL); } } return (0); /* success */ } if (desc->bDescriptorType != UDESC_CONFIG) return (USB_ERR_INVAL); if (desc->bLength < sizeof(*(ues->cd))) return (USB_ERR_INVAL); ues = udev->scratch.hw_ep_scratch; memset(ues, 0, sizeof(*ues)); ues->ep_max = ues->ep; ues->cd = (void *)desc; ues->methods = methods; ues->udev = udev; /* Get all the endpoints we need */ if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 0) || usb_hw_ep_get_needs(ues, UE_INTERRUPT, 0) || usb_hw_ep_get_needs(ues, UE_CONTROL, 0) || usb_hw_ep_get_needs(ues, UE_BULK, 0)) { DPRINTFN(0, "Could not get needs\n"); return (USB_ERR_INVAL); } for (ep = ues->ep; ep != ues->ep_max; ep++) { while (ep->needs_in || ep->needs_out) { /* * First try to use a simplex endpoint. * Then try to use a duplex endpoint. */ if (usb_hw_ep_find_match(ues, ep, 1) && usb_hw_ep_find_match(ues, ep, 0)) { DPRINTFN(0, "Could not find match\n"); return (USB_ERR_INVAL); } } } ues->ep_max = ues->ep; /* Update all endpoint addresses */ if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 1) || usb_hw_ep_get_needs(ues, UE_INTERRUPT, 1) || usb_hw_ep_get_needs(ues, UE_CONTROL, 1) || usb_hw_ep_get_needs(ues, UE_BULK, 1)) { DPRINTFN(0, "Could not update endpoint address\n"); return (USB_ERR_INVAL); } return (0); /* success */ } /*------------------------------------------------------------------------* * usb_temp_get_tdd * * Returns: * NULL: No USB template device descriptor found. * Else: Pointer to the USB template device descriptor. *------------------------------------------------------------------------*/ static const struct usb_temp_device_desc * usb_temp_get_tdd(struct usb_device *udev) { if (udev->usb_template_ptr == NULL) { return (NULL); } return (udev->usb_template_ptr->tdd); } /*------------------------------------------------------------------------* * usb_temp_get_device_desc * * Returns: * NULL: No USB device descriptor found. * Else: Pointer to USB device descriptor. *------------------------------------------------------------------------*/ static void * usb_temp_get_device_desc(struct usb_device *udev) { struct usb_device_descriptor *dd; if (udev->usb_template_ptr == NULL) { return (NULL); } dd = &udev->usb_template_ptr->udd; if (dd->bDescriptorType != UDESC_DEVICE) { /* sanity check failed */ return (NULL); } return (dd); } /*------------------------------------------------------------------------* * usb_temp_get_qualifier_desc * * Returns: * NULL: No USB device_qualifier descriptor found. * Else: Pointer to USB device_qualifier descriptor. *------------------------------------------------------------------------*/ static void * usb_temp_get_qualifier_desc(struct usb_device *udev) { struct usb_device_qualifier *dq; if (udev->usb_template_ptr == NULL) { return (NULL); } dq = &udev->usb_template_ptr->udq; if (dq->bDescriptorType != UDESC_DEVICE_QUALIFIER) { /* sanity check failed */ return (NULL); } return (dq); } /*------------------------------------------------------------------------* * usb_temp_get_config_desc * * Returns: * NULL: No USB config descriptor found. * Else: Pointer to USB config descriptor having index "index". *------------------------------------------------------------------------*/ static void * usb_temp_get_config_desc(struct usb_device *udev, uint16_t *pLength, uint8_t index) { struct usb_device_descriptor *dd; struct usb_config_descriptor *cd; uint16_t temp; if (udev->usb_template_ptr == NULL) { return (NULL); } dd = &udev->usb_template_ptr->udd; cd = (void *)(udev->usb_template_ptr + 1); if (index >= dd->bNumConfigurations) { /* out of range */ return (NULL); } while (index--) { if (cd->bDescriptorType != UDESC_CONFIG) { /* sanity check failed */ return (NULL); } temp = UGETW(cd->wTotalLength); cd = USB_ADD_BYTES(cd, temp); } if (pLength) { *pLength = UGETW(cd->wTotalLength); } return (cd); } /*------------------------------------------------------------------------* * usb_temp_get_vendor_desc * * Returns: * NULL: No vendor descriptor found. * Else: Pointer to a vendor descriptor. *------------------------------------------------------------------------*/ static const void * usb_temp_get_vendor_desc(struct usb_device *udev, const struct usb_device_request *req, uint16_t *plen) { const struct usb_temp_device_desc *tdd; tdd = usb_temp_get_tdd(udev); if (tdd == NULL) { return (NULL); } if (tdd->getVendorDesc == NULL) { return (NULL); } return ((tdd->getVendorDesc) (req, plen)); } /*------------------------------------------------------------------------* * usb_temp_get_string_desc * * Returns: * NULL: No string descriptor found. * Else: Pointer to a string descriptor. *------------------------------------------------------------------------*/ static const void * usb_temp_get_string_desc(struct usb_device *udev, uint16_t lang_id, uint8_t string_index) { const struct usb_temp_device_desc *tdd; tdd = usb_temp_get_tdd(udev); if (tdd == NULL) { return (NULL); } if (tdd->getStringDesc == NULL) { return (NULL); } return ((tdd->getStringDesc) (lang_id, string_index)); } /*------------------------------------------------------------------------* * usb_temp_get_hub_desc * * Returns: * NULL: No USB HUB descriptor found. * Else: Pointer to a USB HUB descriptor. *------------------------------------------------------------------------*/ static const void * usb_temp_get_hub_desc(struct usb_device *udev) { return (NULL); /* needs to be implemented */ } /*------------------------------------------------------------------------* * usb_temp_get_desc * * This function is a demultiplexer for local USB device side control * endpoint requests. *------------------------------------------------------------------------*/ static usb_error_t usb_temp_get_desc(struct usb_device *udev, struct usb_device_request *req, const void **pPtr, uint16_t *pLength) { const uint8_t *buf; uint16_t len; buf = NULL; len = 0; switch (req->bmRequestType) { case UT_READ_DEVICE: switch (req->bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_descriptor; default: goto tr_stalled; } case UT_READ_CLASS_DEVICE: switch (req->bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_class_descriptor; default: goto tr_stalled; } default: goto tr_stalled; } tr_handle_get_descriptor: switch (req->wValue[1]) { case UDESC_DEVICE: if (req->wValue[0]) { goto tr_stalled; } buf = usb_temp_get_device_desc(udev); goto tr_valid; case UDESC_DEVICE_QUALIFIER: if (udev->speed != USB_SPEED_HIGH) { goto tr_stalled; } if (req->wValue[0]) { goto tr_stalled; } buf = usb_temp_get_qualifier_desc(udev); goto tr_valid; case UDESC_OTHER_SPEED_CONFIGURATION: if (udev->speed != USB_SPEED_HIGH) { goto tr_stalled; } case UDESC_CONFIG: buf = usb_temp_get_config_desc(udev, &len, req->wValue[0]); goto tr_valid; case UDESC_STRING: buf = usb_temp_get_string_desc(udev, UGETW(req->wIndex), req->wValue[0]); goto tr_valid; default: goto tr_stalled; } tr_handle_get_class_descriptor: if (req->wValue[0]) { goto tr_stalled; } buf = usb_temp_get_hub_desc(udev); goto tr_valid; tr_valid: if (buf == NULL) goto tr_stalled; if (len == 0) len = buf[0]; *pPtr = buf; *pLength = len; return (0); /* success */ tr_stalled: /* try to get a vendor specific descriptor */ len = 0; buf = usb_temp_get_vendor_desc(udev, req, &len); if (buf != NULL) goto tr_valid; *pPtr = NULL; *pLength = 0; return (0); /* we ignore failures */ } /*------------------------------------------------------------------------* * usb_temp_setup * * This function generates USB descriptors according to the given USB * template device descriptor. It will also try to figure out the best * matching endpoint addresses using the hardware endpoint profiles. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usb_temp_setup(struct usb_device *udev, const struct usb_temp_device_desc *tdd) { struct usb_temp_setup *uts; void *buf; usb_error_t error; uint8_t n; uint8_t do_unlock; /* be NULL safe */ if (tdd == NULL) return (0); /* Protect scratch area */ - do_unlock = usbd_enum_lock(udev); + do_unlock = usbd_ctrl_lock(udev); uts = udev->scratch.temp_setup; memset(uts, 0, sizeof(*uts)); uts->usb_speed = udev->speed; uts->self_powered = udev->flags.self_powered; /* first pass */ usb_make_device_desc(uts, tdd); if (uts->err) { /* some error happened */ goto done; } /* sanity check */ if (uts->size == 0) { uts->err = USB_ERR_INVAL; goto done; } /* allocate zeroed memory */ uts->buf = usbd_alloc_config_desc(udev, uts->size); /* * Allow malloc() to return NULL regardless of M_WAITOK flag. * This helps when porting the software to non-FreeBSD * systems. */ if (uts->buf == NULL) { /* could not allocate memory */ uts->err = USB_ERR_NOMEM; goto done; } /* second pass */ uts->size = 0; usb_make_device_desc(uts, tdd); /* * Store a pointer to our descriptors: */ udev->usb_template_ptr = uts->buf; if (uts->err) { /* some error happened during second pass */ goto done; } /* * Resolve all endpoint addresses ! */ buf = usb_temp_get_device_desc(udev); uts->err = usb_hw_ep_resolve(udev, buf); if (uts->err) { DPRINTFN(0, "Could not resolve endpoints for " "Device Descriptor, error = %s\n", usbd_errstr(uts->err)); goto done; } for (n = 0;; n++) { buf = usb_temp_get_config_desc(udev, NULL, n); if (buf == NULL) { break; } uts->err = usb_hw_ep_resolve(udev, buf); if (uts->err) { DPRINTFN(0, "Could not resolve endpoints for " "Config Descriptor %u, error = %s\n", n, usbd_errstr(uts->err)); goto done; } } done: error = uts->err; if (error) usb_temp_unsetup(udev); if (do_unlock) - usbd_enum_unlock(udev); + usbd_ctrl_unlock(udev); return (error); } /*------------------------------------------------------------------------* * usb_temp_unsetup * * This function frees any memory associated with the currently * setup template, if any. *------------------------------------------------------------------------*/ void usb_temp_unsetup(struct usb_device *udev) { usbd_free_config_desc(udev, udev->usb_template_ptr); udev->usb_template_ptr = NULL; } static usb_error_t usb_temp_setup_by_index(struct usb_device *udev, uint16_t index) { usb_error_t err; switch (index) { case USB_TEMP_MSC: err = usb_temp_setup(udev, &usb_template_msc); break; case USB_TEMP_CDCE: err = usb_temp_setup(udev, &usb_template_cdce); break; case USB_TEMP_MTP: err = usb_temp_setup(udev, &usb_template_mtp); break; case USB_TEMP_MODEM: err = usb_temp_setup(udev, &usb_template_modem); break; case USB_TEMP_AUDIO: err = usb_temp_setup(udev, &usb_template_audio); break; case USB_TEMP_KBD: err = usb_temp_setup(udev, &usb_template_kbd); break; case USB_TEMP_MOUSE: err = usb_temp_setup(udev, &usb_template_mouse); break; case USB_TEMP_PHONE: err = usb_temp_setup(udev, &usb_template_phone); break; case USB_TEMP_SERIALNET: err = usb_temp_setup(udev, &usb_template_serialnet); break; case USB_TEMP_MIDI: err = usb_temp_setup(udev, &usb_template_midi); break; default: return (USB_ERR_INVAL); } return (err); } static void usb_temp_init(void *arg) { /* register our functions */ usb_temp_get_desc_p = &usb_temp_get_desc; usb_temp_setup_by_index_p = &usb_temp_setup_by_index; usb_temp_unsetup_p = &usb_temp_unsetup; } SYSINIT(usb_temp_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb_temp_init, NULL); SYSUNINIT(usb_temp_unload, SI_SUB_LOCK, SI_ORDER_ANY, usb_temp_unload, NULL); Index: projects/clang390-import/sys/dev/usb/usb_device.c =================================================================== --- projects/clang390-import/sys/dev/usb/usb_device.c (revision 305430) +++ projects/clang390-import/sys/dev/usb/usb_device.c (revision 305431) @@ -1,2911 +1,2947 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if USB_HAVE_UGEN #include #endif #include "usbdevs.h" #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include #include #include #if USB_HAVE_UGEN #include #include #endif #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ /* function prototypes */ static void usb_init_endpoint(struct usb_device *, uint8_t, struct usb_endpoint_descriptor *, struct usb_endpoint_ss_comp_descriptor *, struct usb_endpoint *); static void usb_unconfigure(struct usb_device *, uint8_t); static void usb_detach_device_sub(struct usb_device *, device_t *, char **, uint8_t); static uint8_t usb_probe_and_attach_sub(struct usb_device *, struct usb_attach_arg *); static void usb_init_attach_arg(struct usb_device *, struct usb_attach_arg *); static void usb_suspend_resume_sub(struct usb_device *, device_t, uint8_t); static usb_proc_callback_t usbd_clear_stall_proc; static usb_error_t usb_config_parse(struct usb_device *, uint8_t, uint8_t); static void usbd_set_device_strings(struct usb_device *); #if USB_HAVE_DEVCTL static void usb_notify_addq(const char *type, struct usb_device *); #endif #if USB_HAVE_UGEN static void usb_fifo_free_wrap(struct usb_device *, uint8_t, uint8_t); static void usb_cdev_create(struct usb_device *); static void usb_cdev_free(struct usb_device *); #endif /* This variable is global to allow easy access to it: */ #ifdef USB_TEMPLATE int usb_template = USB_TEMPLATE; #else int usb_template; #endif SYSCTL_INT(_hw_usb, OID_AUTO, template, CTLFLAG_RWTUN, &usb_template, 0, "Selected USB device side template"); /* English is default language */ static int usb_lang_id = 0x0009; static int usb_lang_mask = 0x00FF; SYSCTL_INT(_hw_usb, OID_AUTO, usb_lang_id, CTLFLAG_RWTUN, &usb_lang_id, 0, "Preferred USB language ID"); SYSCTL_INT(_hw_usb, OID_AUTO, usb_lang_mask, CTLFLAG_RWTUN, &usb_lang_mask, 0, "Preferred USB language mask"); static const char* statestr[USB_STATE_MAX] = { [USB_STATE_DETACHED] = "DETACHED", [USB_STATE_ATTACHED] = "ATTACHED", [USB_STATE_POWERED] = "POWERED", [USB_STATE_ADDRESSED] = "ADDRESSED", [USB_STATE_CONFIGURED] = "CONFIGURED", }; const char * usb_statestr(enum usb_dev_state state) { return ((state < USB_STATE_MAX) ? statestr[state] : "UNKNOWN"); } const char * usb_get_manufacturer(struct usb_device *udev) { return (udev->manufacturer ? udev->manufacturer : "Unknown"); } const char * usb_get_product(struct usb_device *udev) { return (udev->product ? udev->product : ""); } const char * usb_get_serial(struct usb_device *udev) { return (udev->serial ? udev->serial : ""); } /*------------------------------------------------------------------------* * usbd_get_ep_by_addr * * This function searches for an USB ep by endpoint address and * direction. * * Returns: * NULL: Failure * Else: Success *------------------------------------------------------------------------*/ struct usb_endpoint * usbd_get_ep_by_addr(struct usb_device *udev, uint8_t ea_val) { struct usb_endpoint *ep = udev->endpoints; struct usb_endpoint *ep_end = udev->endpoints + udev->endpoints_max; enum { EA_MASK = (UE_DIR_IN | UE_DIR_OUT | UE_ADDR), }; /* * According to the USB specification not all bits are used * for the endpoint address. Keep defined bits only: */ ea_val &= EA_MASK; /* * Iterate across all the USB endpoints searching for a match * based on the endpoint address: */ for (; ep != ep_end; ep++) { if (ep->edesc == NULL) { continue; } /* do the mask and check the value */ if ((ep->edesc->bEndpointAddress & EA_MASK) == ea_val) { goto found; } } /* * The default endpoint is always present and is checked separately: */ if ((udev->ctrl_ep.edesc != NULL) && ((udev->ctrl_ep.edesc->bEndpointAddress & EA_MASK) == ea_val)) { ep = &udev->ctrl_ep; goto found; } return (NULL); found: return (ep); } /*------------------------------------------------------------------------* * usbd_get_endpoint * * This function searches for an USB endpoint based on the information * given by the passed "struct usb_config" pointer. * * Return values: * NULL: No match. * Else: Pointer to "struct usb_endpoint". *------------------------------------------------------------------------*/ struct usb_endpoint * usbd_get_endpoint(struct usb_device *udev, uint8_t iface_index, const struct usb_config *setup) { struct usb_endpoint *ep = udev->endpoints; struct usb_endpoint *ep_end = udev->endpoints + udev->endpoints_max; uint8_t index = setup->ep_index; uint8_t ea_mask; uint8_t ea_val; uint8_t type_mask; uint8_t type_val; DPRINTFN(10, "udev=%p iface_index=%d address=0x%x " "type=0x%x dir=0x%x index=%d\n", udev, iface_index, setup->endpoint, setup->type, setup->direction, setup->ep_index); /* check USB mode */ if (setup->usb_mode != USB_MODE_DUAL && udev->flags.usb_mode != setup->usb_mode) { /* wrong mode - no endpoint */ return (NULL); } /* setup expected endpoint direction mask and value */ if (setup->direction == UE_DIR_RX) { ea_mask = (UE_DIR_IN | UE_DIR_OUT); ea_val = (udev->flags.usb_mode == USB_MODE_DEVICE) ? UE_DIR_OUT : UE_DIR_IN; } else if (setup->direction == UE_DIR_TX) { ea_mask = (UE_DIR_IN | UE_DIR_OUT); ea_val = (udev->flags.usb_mode == USB_MODE_DEVICE) ? UE_DIR_IN : UE_DIR_OUT; } else if (setup->direction == UE_DIR_ANY) { /* match any endpoint direction */ ea_mask = 0; ea_val = 0; } else { /* match the given endpoint direction */ ea_mask = (UE_DIR_IN | UE_DIR_OUT); ea_val = (setup->direction & (UE_DIR_IN | UE_DIR_OUT)); } /* setup expected endpoint address */ if (setup->endpoint == UE_ADDR_ANY) { /* match any endpoint address */ } else { /* match the given endpoint address */ ea_mask |= UE_ADDR; ea_val |= (setup->endpoint & UE_ADDR); } /* setup expected endpoint type */ if (setup->type == UE_BULK_INTR) { /* this will match BULK and INTERRUPT endpoints */ type_mask = 2; type_val = 2; } else if (setup->type == UE_TYPE_ANY) { /* match any endpoint type */ type_mask = 0; type_val = 0; } else { /* match the given endpoint type */ type_mask = UE_XFERTYPE; type_val = (setup->type & UE_XFERTYPE); } /* * Iterate across all the USB endpoints searching for a match * based on the endpoint address. Note that we are searching * the endpoints from the beginning of the "udev->endpoints" array. */ for (; ep != ep_end; ep++) { if ((ep->edesc == NULL) || (ep->iface_index != iface_index)) { continue; } /* do the masks and check the values */ if (((ep->edesc->bEndpointAddress & ea_mask) == ea_val) && ((ep->edesc->bmAttributes & type_mask) == type_val)) { if (!index--) { goto found; } } } /* * Match against default endpoint last, so that "any endpoint", "any * address" and "any direction" returns the first endpoint of the * interface. "iface_index" and "direction" is ignored: */ if ((udev->ctrl_ep.edesc != NULL) && ((udev->ctrl_ep.edesc->bEndpointAddress & ea_mask) == ea_val) && ((udev->ctrl_ep.edesc->bmAttributes & type_mask) == type_val) && (!index)) { ep = &udev->ctrl_ep; goto found; } return (NULL); found: return (ep); } /*------------------------------------------------------------------------* * usbd_interface_count * * This function stores the number of USB interfaces excluding * alternate settings, which the USB config descriptor reports into * the unsigned 8-bit integer pointed to by "count". * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_interface_count(struct usb_device *udev, uint8_t *count) { if (udev->cdesc == NULL) { *count = 0; return (USB_ERR_NOT_CONFIGURED); } *count = udev->ifaces_max; return (USB_ERR_NORMAL_COMPLETION); } /*------------------------------------------------------------------------* * usb_init_endpoint * * This function will initialise the USB endpoint structure pointed to by * the "endpoint" argument. The structure pointed to by "endpoint" must be * zeroed before calling this function. *------------------------------------------------------------------------*/ static void usb_init_endpoint(struct usb_device *udev, uint8_t iface_index, struct usb_endpoint_descriptor *edesc, struct usb_endpoint_ss_comp_descriptor *ecomp, struct usb_endpoint *ep) { const struct usb_bus_methods *methods; usb_stream_t x; methods = udev->bus->methods; (methods->endpoint_init) (udev, edesc, ep); /* initialise USB endpoint structure */ ep->edesc = edesc; ep->ecomp = ecomp; ep->iface_index = iface_index; /* setup USB stream queues */ for (x = 0; x != USB_MAX_EP_STREAMS; x++) { TAILQ_INIT(&ep->endpoint_q[x].head); ep->endpoint_q[x].command = &usbd_pipe_start; } /* the pipe is not supported by the hardware */ if (ep->methods == NULL) return; /* check for SUPER-speed streams mode endpoint */ if (udev->speed == USB_SPEED_SUPER && ecomp != NULL && (edesc->bmAttributes & UE_XFERTYPE) == UE_BULK && (UE_GET_BULK_STREAMS(ecomp->bmAttributes) != 0)) { usbd_set_endpoint_mode(udev, ep, USB_EP_MODE_STREAMS); } else { usbd_set_endpoint_mode(udev, ep, USB_EP_MODE_DEFAULT); } /* clear stall, if any */ if (methods->clear_stall != NULL) { USB_BUS_LOCK(udev->bus); (methods->clear_stall) (udev, ep); USB_BUS_UNLOCK(udev->bus); } } /*-----------------------------------------------------------------------* * usb_endpoint_foreach * * This function will iterate all the USB endpoints except the control * endpoint. This function is NULL safe. * * Return values: * NULL: End of USB endpoints * Else: Pointer to next USB endpoint *------------------------------------------------------------------------*/ struct usb_endpoint * usb_endpoint_foreach(struct usb_device *udev, struct usb_endpoint *ep) { struct usb_endpoint *ep_end; /* be NULL safe */ if (udev == NULL) return (NULL); ep_end = udev->endpoints + udev->endpoints_max; /* get next endpoint */ if (ep == NULL) ep = udev->endpoints; else ep++; /* find next allocated ep */ while (ep != ep_end) { if (ep->edesc != NULL) return (ep); ep++; } return (NULL); } /*------------------------------------------------------------------------* * usb_wait_pending_refs * * This function will wait for any USB references to go away before * returning. This function is used before freeing a USB device. *------------------------------------------------------------------------*/ static void usb_wait_pending_refs(struct usb_device *udev) { #if USB_HAVE_UGEN DPRINTF("Refcount = %d\n", (int)udev->refcount); mtx_lock(&usb_ref_lock); udev->refcount--; while (1) { /* wait for any pending references to go away */ if (udev->refcount == 0) { /* prevent further refs being taken, if any */ udev->refcount = USB_DEV_REF_MAX; break; } cv_wait(&udev->ref_cv, &usb_ref_lock); } mtx_unlock(&usb_ref_lock); #endif } /*------------------------------------------------------------------------* * usb_unconfigure * * This function will free all USB interfaces and USB endpoints belonging * to an USB device. * * Flag values, see "USB_UNCFG_FLAG_XXX". *------------------------------------------------------------------------*/ static void usb_unconfigure(struct usb_device *udev, uint8_t flag) { uint8_t do_unlock; /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); /* detach all interface drivers */ usb_detach_device(udev, USB_IFACE_INDEX_ANY, flag); #if USB_HAVE_UGEN /* free all FIFOs except control endpoint FIFOs */ usb_fifo_free_wrap(udev, USB_IFACE_INDEX_ANY, flag); /* * Free all cdev's, if any. */ usb_cdev_free(udev); #endif #if USB_HAVE_COMPAT_LINUX /* free Linux compat device, if any */ if (udev->linux_endpoint_start != NULL) { usb_linux_free_device_p(udev); udev->linux_endpoint_start = NULL; } #endif usb_config_parse(udev, USB_IFACE_INDEX_ANY, USB_CFG_FREE); /* free "cdesc" after "ifaces" and "endpoints", if any */ if (udev->cdesc != NULL) { if (udev->flags.usb_mode != USB_MODE_DEVICE) usbd_free_config_desc(udev, udev->cdesc); udev->cdesc = NULL; } /* set unconfigured state */ udev->curr_config_no = USB_UNCONFIG_NO; udev->curr_config_index = USB_UNCONFIG_INDEX; if (do_unlock) usbd_enum_unlock(udev); } /*------------------------------------------------------------------------* * usbd_set_config_index * * This function selects configuration by index, independent of the * actual configuration number. This function should not be used by * USB drivers. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_set_config_index(struct usb_device *udev, uint8_t index) { struct usb_status ds; struct usb_config_descriptor *cdp; uint16_t power; uint16_t max_power; uint8_t selfpowered; uint8_t do_unlock; usb_error_t err; DPRINTFN(6, "udev=%p index=%d\n", udev, index); /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); usb_unconfigure(udev, 0); if (index == USB_UNCONFIG_INDEX) { /* * Leave unallocated when unconfiguring the * device. "usb_unconfigure()" will also reset * the current config number and index. */ err = usbd_req_set_config(udev, NULL, USB_UNCONFIG_NO); if (udev->state == USB_STATE_CONFIGURED) usb_set_device_state(udev, USB_STATE_ADDRESSED); goto done; } /* get the full config descriptor */ if (udev->flags.usb_mode == USB_MODE_DEVICE) { /* save some memory */ err = usbd_req_get_descriptor_ptr(udev, &cdp, (UDESC_CONFIG << 8) | index); } else { /* normal request */ err = usbd_req_get_config_desc_full(udev, NULL, &cdp, index); } if (err) { goto done; } /* set the new config descriptor */ udev->cdesc = cdp; /* Figure out if the device is self or bus powered. */ selfpowered = 0; if ((!udev->flags.uq_bus_powered) && (cdp->bmAttributes & UC_SELF_POWERED) && (udev->flags.usb_mode == USB_MODE_HOST)) { /* May be self powered. */ if (cdp->bmAttributes & UC_BUS_POWERED) { /* Must ask device. */ err = usbd_req_get_device_status(udev, NULL, &ds); if (err) { DPRINTFN(0, "could not read " "device status: %s\n", usbd_errstr(err)); } else if (UGETW(ds.wStatus) & UDS_SELF_POWERED) { selfpowered = 1; } DPRINTF("status=0x%04x \n", UGETW(ds.wStatus)); } else selfpowered = 1; } DPRINTF("udev=%p cdesc=%p (addr %d) cno=%d attr=0x%02x, " "selfpowered=%d, power=%d\n", udev, cdp, udev->address, cdp->bConfigurationValue, cdp->bmAttributes, selfpowered, cdp->bMaxPower * 2); /* Check if we have enough power. */ power = cdp->bMaxPower * 2; if (udev->parent_hub) { max_power = udev->parent_hub->hub->portpower; } else { max_power = USB_MAX_POWER; } if (power > max_power) { DPRINTFN(0, "power exceeded %d > %d\n", power, max_power); err = USB_ERR_NO_POWER; goto done; } /* Only update "self_powered" in USB Host Mode */ if (udev->flags.usb_mode == USB_MODE_HOST) { udev->flags.self_powered = selfpowered; } udev->power = power; udev->curr_config_no = cdp->bConfigurationValue; udev->curr_config_index = index; usb_set_device_state(udev, USB_STATE_CONFIGURED); /* Set the actual configuration value. */ err = usbd_req_set_config(udev, NULL, cdp->bConfigurationValue); if (err) { goto done; } err = usb_config_parse(udev, USB_IFACE_INDEX_ANY, USB_CFG_ALLOC); if (err) { goto done; } err = usb_config_parse(udev, USB_IFACE_INDEX_ANY, USB_CFG_INIT); if (err) { goto done; } #if USB_HAVE_UGEN /* create device nodes for each endpoint */ usb_cdev_create(udev); #endif done: DPRINTF("error=%s\n", usbd_errstr(err)); if (err) { usb_unconfigure(udev, 0); } if (do_unlock) usbd_enum_unlock(udev); return (err); } /*------------------------------------------------------------------------* * usb_config_parse * * This function will allocate and free USB interfaces and USB endpoints, * parse the USB configuration structure and initialise the USB endpoints * and interfaces. If "iface_index" is not equal to * "USB_IFACE_INDEX_ANY" then the "cmd" parameter is the * alternate_setting to be selected for the given interface. Else the * "cmd" parameter is defined by "USB_CFG_XXX". "iface_index" can be * "USB_IFACE_INDEX_ANY" or a valid USB interface index. This function * is typically called when setting the configuration or when setting * an alternate interface. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static usb_error_t usb_config_parse(struct usb_device *udev, uint8_t iface_index, uint8_t cmd) { struct usb_idesc_parse_state ips; struct usb_interface_descriptor *id; struct usb_endpoint_descriptor *ed; struct usb_interface *iface; struct usb_endpoint *ep; usb_error_t err; uint8_t ep_curr; uint8_t ep_max; uint8_t temp; uint8_t do_init; uint8_t alt_index; if (iface_index != USB_IFACE_INDEX_ANY) { /* parameter overload */ alt_index = cmd; cmd = USB_CFG_INIT; } else { /* not used */ alt_index = 0; } err = 0; DPRINTFN(5, "iface_index=%d cmd=%d\n", iface_index, cmd); if (cmd == USB_CFG_FREE) goto cleanup; if (cmd == USB_CFG_INIT) { sx_assert(&udev->enum_sx, SA_LOCKED); /* check for in-use endpoints */ ep = udev->endpoints; ep_max = udev->endpoints_max; while (ep_max--) { /* look for matching endpoints */ if ((iface_index == USB_IFACE_INDEX_ANY) || (iface_index == ep->iface_index)) { if (ep->refcount_alloc != 0) { /* * This typically indicates a * more serious error. */ err = USB_ERR_IN_USE; } else { /* reset endpoint */ memset(ep, 0, sizeof(*ep)); /* make sure we don't zero the endpoint again */ ep->iface_index = USB_IFACE_INDEX_ANY; } } ep++; } if (err) return (err); } memset(&ips, 0, sizeof(ips)); ep_curr = 0; ep_max = 0; while ((id = usb_idesc_foreach(udev->cdesc, &ips))) { iface = udev->ifaces + ips.iface_index; /* check for specific interface match */ if (cmd == USB_CFG_INIT) { if ((iface_index != USB_IFACE_INDEX_ANY) && (iface_index != ips.iface_index)) { /* wrong interface */ do_init = 0; } else if (alt_index != ips.iface_index_alt) { /* wrong alternate setting */ do_init = 0; } else { /* initialise interface */ do_init = 1; } } else do_init = 0; /* check for new interface */ if (ips.iface_index_alt == 0) { /* update current number of endpoints */ ep_curr = ep_max; } /* check for init */ if (do_init) { /* setup the USB interface structure */ iface->idesc = id; /* set alternate index */ iface->alt_index = alt_index; /* set default interface parent */ if (iface_index == USB_IFACE_INDEX_ANY) { iface->parent_iface_index = USB_IFACE_INDEX_ANY; } } DPRINTFN(5, "found idesc nendpt=%d\n", id->bNumEndpoints); ed = (struct usb_endpoint_descriptor *)id; temp = ep_curr; /* iterate all the endpoint descriptors */ while ((ed = usb_edesc_foreach(udev->cdesc, ed))) { /* check if endpoint limit has been reached */ if (temp >= USB_MAX_EP_UNITS) { DPRINTF("Endpoint limit reached\n"); break; } ep = udev->endpoints + temp; if (do_init) { void *ecomp; ecomp = usb_ed_comp_foreach(udev->cdesc, (void *)ed); if (ecomp != NULL) DPRINTFN(5, "Found endpoint companion descriptor\n"); usb_init_endpoint(udev, ips.iface_index, ed, ecomp, ep); } temp ++; /* find maximum number of endpoints */ if (ep_max < temp) ep_max = temp; } } /* NOTE: It is valid to have no interfaces and no endpoints! */ if (cmd == USB_CFG_ALLOC) { udev->ifaces_max = ips.iface_index; #if (USB_HAVE_FIXED_IFACE == 0) udev->ifaces = NULL; if (udev->ifaces_max != 0) { udev->ifaces = malloc(sizeof(*iface) * udev->ifaces_max, M_USB, M_WAITOK | M_ZERO); if (udev->ifaces == NULL) { err = USB_ERR_NOMEM; goto done; } } #endif #if (USB_HAVE_FIXED_ENDPOINT == 0) if (ep_max != 0) { udev->endpoints = malloc(sizeof(*ep) * ep_max, M_USB, M_WAITOK | M_ZERO); if (udev->endpoints == NULL) { err = USB_ERR_NOMEM; goto done; } } else { udev->endpoints = NULL; } #endif USB_BUS_LOCK(udev->bus); udev->endpoints_max = ep_max; /* reset any ongoing clear-stall */ udev->ep_curr = NULL; USB_BUS_UNLOCK(udev->bus); } #if (USB_HAVE_FIXED_IFACE == 0) || (USB_HAVE_FIXED_ENDPOINT == 0) done: #endif if (err) { if (cmd == USB_CFG_ALLOC) { cleanup: USB_BUS_LOCK(udev->bus); udev->endpoints_max = 0; /* reset any ongoing clear-stall */ udev->ep_curr = NULL; USB_BUS_UNLOCK(udev->bus); #if (USB_HAVE_FIXED_IFACE == 0) free(udev->ifaces, M_USB); udev->ifaces = NULL; #endif #if (USB_HAVE_FIXED_ENDPOINT == 0) free(udev->endpoints, M_USB); udev->endpoints = NULL; #endif udev->ifaces_max = 0; } } return (err); } /*------------------------------------------------------------------------* * usbd_set_alt_interface_index * * This function will select an alternate interface index for the * given interface index. The interface should not be in use when this * function is called. That means there should not be any open USB * transfers. Else an error is returned. If the alternate setting is * already set this function will simply return success. This function * is called in Host mode and Device mode! * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_set_alt_interface_index(struct usb_device *udev, uint8_t iface_index, uint8_t alt_index) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); usb_error_t err; uint8_t do_unlock; /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); if (iface == NULL) { err = USB_ERR_INVAL; goto done; } if (iface->alt_index == alt_index) { /* * Optimise away duplicate setting of * alternate setting in USB Host Mode! */ err = 0; goto done; } #if USB_HAVE_UGEN /* * Free all generic FIFOs for this interface, except control * endpoint FIFOs: */ usb_fifo_free_wrap(udev, iface_index, 0); #endif err = usb_config_parse(udev, iface_index, alt_index); if (err) { goto done; } if (iface->alt_index != alt_index) { /* the alternate setting does not exist */ err = USB_ERR_INVAL; goto done; } err = usbd_req_set_alt_interface_no(udev, NULL, iface_index, iface->idesc->bAlternateSetting); done: if (do_unlock) usbd_enum_unlock(udev); return (err); } /*------------------------------------------------------------------------* * usbd_set_endpoint_stall * * This function is used to make a BULK or INTERRUPT endpoint send * STALL tokens in USB device mode. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_set_endpoint_stall(struct usb_device *udev, struct usb_endpoint *ep, uint8_t do_stall) { struct usb_xfer *xfer; usb_stream_t x; uint8_t et; uint8_t was_stalled; if (ep == NULL) { /* nothing to do */ DPRINTF("Cannot find endpoint\n"); /* * Pretend that the clear or set stall request is * successful else some USB host stacks can do * strange things, especially when a control endpoint * stalls. */ return (0); } et = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((et != UE_BULK) && (et != UE_INTERRUPT)) { /* * Should not stall control * nor isochronous endpoints. */ DPRINTF("Invalid endpoint\n"); return (0); } USB_BUS_LOCK(udev->bus); /* store current stall state */ was_stalled = ep->is_stalled; /* check for no change */ if (was_stalled && do_stall) { /* if the endpoint is already stalled do nothing */ USB_BUS_UNLOCK(udev->bus); DPRINTF("No change\n"); return (0); } /* set stalled state */ ep->is_stalled = 1; if (do_stall || (!was_stalled)) { if (!was_stalled) { for (x = 0; x != USB_MAX_EP_STREAMS; x++) { /* lookup the current USB transfer, if any */ xfer = ep->endpoint_q[x].curr; if (xfer != NULL) { /* * The "xfer_stall" method * will complete the USB * transfer like in case of a * timeout setting the error * code "USB_ERR_STALLED". */ (udev->bus->methods->xfer_stall) (xfer); } } } (udev->bus->methods->set_stall) (udev, ep, &do_stall); } if (!do_stall) { ep->toggle_next = 0; /* reset data toggle */ ep->is_stalled = 0; /* clear stalled state */ (udev->bus->methods->clear_stall) (udev, ep); /* start the current or next transfer, if any */ for (x = 0; x != USB_MAX_EP_STREAMS; x++) { usb_command_wrapper(&ep->endpoint_q[x], ep->endpoint_q[x].curr); } } USB_BUS_UNLOCK(udev->bus); return (0); } /*------------------------------------------------------------------------* * usb_reset_iface_endpoints - used in USB device side mode *------------------------------------------------------------------------*/ usb_error_t usb_reset_iface_endpoints(struct usb_device *udev, uint8_t iface_index) { struct usb_endpoint *ep; struct usb_endpoint *ep_end; ep = udev->endpoints; ep_end = udev->endpoints + udev->endpoints_max; for (; ep != ep_end; ep++) { if ((ep->edesc == NULL) || (ep->iface_index != iface_index)) { continue; } /* simulate a clear stall from the peer */ usbd_set_endpoint_stall(udev, ep, 0); } return (0); } /*------------------------------------------------------------------------* * usb_detach_device_sub * * This function will try to detach an USB device. If it fails a panic * will result. * * Flag values, see "USB_UNCFG_FLAG_XXX". *------------------------------------------------------------------------*/ static void usb_detach_device_sub(struct usb_device *udev, device_t *ppdev, char **ppnpinfo, uint8_t flag) { device_t dev; char *pnpinfo; int err; dev = *ppdev; if (dev) { /* * NOTE: It is important to clear "*ppdev" before deleting * the child due to some device methods being called late * during the delete process ! */ *ppdev = NULL; if (!rebooting) { device_printf(dev, "at %s, port %d, addr %d " "(disconnected)\n", device_get_nameunit(udev->parent_dev), udev->port_no, udev->address); } if (device_is_attached(dev)) { if (udev->flags.peer_suspended) { err = DEVICE_RESUME(dev); if (err) { device_printf(dev, "Resume failed\n"); } } if (device_detach(dev)) { goto error; } } if (device_delete_child(udev->parent_dev, dev)) { goto error; } } pnpinfo = *ppnpinfo; if (pnpinfo != NULL) { *ppnpinfo = NULL; free(pnpinfo, M_USBDEV); } return; error: /* Detach is not allowed to fail in the USB world */ panic("usb_detach_device_sub: A USB driver would not detach\n"); } /*------------------------------------------------------------------------* * usb_detach_device * * The following function will detach the matching interfaces. * This function is NULL safe. * * Flag values, see "USB_UNCFG_FLAG_XXX". *------------------------------------------------------------------------*/ void usb_detach_device(struct usb_device *udev, uint8_t iface_index, uint8_t flag) { struct usb_interface *iface; uint8_t i; if (udev == NULL) { /* nothing to do */ return; } DPRINTFN(4, "udev=%p\n", udev); sx_assert(&udev->enum_sx, SA_LOCKED); /* * First detach the child to give the child's detach routine a * chance to detach the sub-devices in the correct order. * Then delete the child using "device_delete_child()" which * will detach all sub-devices from the bottom and upwards! */ if (iface_index != USB_IFACE_INDEX_ANY) { i = iface_index; iface_index = i + 1; } else { i = 0; iface_index = USB_IFACE_MAX; } /* do the detach */ for (; i != iface_index; i++) { iface = usbd_get_iface(udev, i); if (iface == NULL) { /* looks like the end of the USB interfaces */ break; } usb_detach_device_sub(udev, &iface->subdev, &iface->pnpinfo, flag); } } /*------------------------------------------------------------------------* * usb_probe_and_attach_sub * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static uint8_t usb_probe_and_attach_sub(struct usb_device *udev, struct usb_attach_arg *uaa) { struct usb_interface *iface; device_t dev; int err; iface = uaa->iface; if (iface->parent_iface_index != USB_IFACE_INDEX_ANY) { /* leave interface alone */ return (0); } dev = iface->subdev; if (dev) { /* clean up after module unload */ if (device_is_attached(dev)) { /* already a device there */ return (0); } /* clear "iface->subdev" as early as possible */ iface->subdev = NULL; if (device_delete_child(udev->parent_dev, dev)) { /* * Panic here, else one can get a double call * to device_detach(). USB devices should * never fail on detach! */ panic("device_delete_child() failed\n"); } } if (uaa->temp_dev == NULL) { /* create a new child */ uaa->temp_dev = device_add_child(udev->parent_dev, NULL, -1); if (uaa->temp_dev == NULL) { device_printf(udev->parent_dev, "Device creation failed\n"); return (1); /* failure */ } device_set_ivars(uaa->temp_dev, uaa); device_quiet(uaa->temp_dev); } /* * Set "subdev" before probe and attach so that "devd" gets * the information it needs. */ iface->subdev = uaa->temp_dev; if (device_probe_and_attach(iface->subdev) == 0) { /* * The USB attach arguments are only available during probe * and attach ! */ uaa->temp_dev = NULL; device_set_ivars(iface->subdev, NULL); if (udev->flags.peer_suspended) { err = DEVICE_SUSPEND(iface->subdev); if (err) device_printf(iface->subdev, "Suspend failed\n"); } return (0); /* success */ } else { /* No USB driver found */ iface->subdev = NULL; } return (1); /* failure */ } /*------------------------------------------------------------------------* * usbd_set_parent_iface * * Using this function will lock the alternate interface setting on an * interface. It is typically used for multi interface drivers. In USB * device side mode it is assumed that the alternate interfaces all * have the same endpoint descriptors. The default parent index value * is "USB_IFACE_INDEX_ANY". Then the alternate setting value is not * locked. *------------------------------------------------------------------------*/ void usbd_set_parent_iface(struct usb_device *udev, uint8_t iface_index, uint8_t parent_index) { struct usb_interface *iface; if (udev == NULL) { /* nothing to do */ return; } iface = usbd_get_iface(udev, iface_index); if (iface != NULL) iface->parent_iface_index = parent_index; } static void usb_init_attach_arg(struct usb_device *udev, struct usb_attach_arg *uaa) { memset(uaa, 0, sizeof(*uaa)); uaa->device = udev; uaa->usb_mode = udev->flags.usb_mode; uaa->port = udev->port_no; uaa->dev_state = UAA_DEV_READY; uaa->info.idVendor = UGETW(udev->ddesc.idVendor); uaa->info.idProduct = UGETW(udev->ddesc.idProduct); uaa->info.bcdDevice = UGETW(udev->ddesc.bcdDevice); uaa->info.bDeviceClass = udev->ddesc.bDeviceClass; uaa->info.bDeviceSubClass = udev->ddesc.bDeviceSubClass; uaa->info.bDeviceProtocol = udev->ddesc.bDeviceProtocol; uaa->info.bConfigIndex = udev->curr_config_index; uaa->info.bConfigNum = udev->curr_config_no; } /*------------------------------------------------------------------------* * usb_probe_and_attach * * This function is called from "uhub_explore_sub()", * "usb_handle_set_config()" and "usb_handle_request()". * * Returns: * 0: Success * Else: A control transfer failed *------------------------------------------------------------------------*/ usb_error_t usb_probe_and_attach(struct usb_device *udev, uint8_t iface_index) { struct usb_attach_arg uaa; struct usb_interface *iface; uint8_t i; uint8_t j; uint8_t do_unlock; if (udev == NULL) { DPRINTF("udev == NULL\n"); return (USB_ERR_INVAL); } /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); if (udev->curr_config_index == USB_UNCONFIG_INDEX) { /* do nothing - no configuration has been set */ goto done; } /* setup USB attach arguments */ usb_init_attach_arg(udev, &uaa); /* * If the whole USB device is targeted, invoke the USB event * handler(s): */ if (iface_index == USB_IFACE_INDEX_ANY) { if (usb_test_quirk(&uaa, UQ_MSC_DYMO_EJECT) != 0 && usb_dymo_eject(udev, 0) == 0) { /* success, mark the udev as disappearing */ uaa.dev_state = UAA_DEV_EJECTING; } EVENTHANDLER_INVOKE(usb_dev_configured, udev, &uaa); if (uaa.dev_state != UAA_DEV_READY) { /* leave device unconfigured */ usb_unconfigure(udev, 0); goto done; } } /* Check if only one interface should be probed: */ if (iface_index != USB_IFACE_INDEX_ANY) { i = iface_index; j = i + 1; } else { i = 0; j = USB_IFACE_MAX; } /* Do the probe and attach */ for (; i != j; i++) { iface = usbd_get_iface(udev, i); if (iface == NULL) { /* * Looks like the end of the USB * interfaces ! */ DPRINTFN(2, "end of interfaces " "at %u\n", i); break; } if (iface->idesc == NULL) { /* no interface descriptor */ continue; } uaa.iface = iface; uaa.info.bInterfaceClass = iface->idesc->bInterfaceClass; uaa.info.bInterfaceSubClass = iface->idesc->bInterfaceSubClass; uaa.info.bInterfaceProtocol = iface->idesc->bInterfaceProtocol; uaa.info.bIfaceIndex = i; uaa.info.bIfaceNum = iface->idesc->bInterfaceNumber; uaa.driver_info = 0; /* reset driver_info */ DPRINTFN(2, "iclass=%u/%u/%u iindex=%u/%u\n", uaa.info.bInterfaceClass, uaa.info.bInterfaceSubClass, uaa.info.bInterfaceProtocol, uaa.info.bIfaceIndex, uaa.info.bIfaceNum); usb_probe_and_attach_sub(udev, &uaa); /* * Remove the leftover child, if any, to enforce that * a new nomatch devd event is generated for the next * interface if no driver is found: */ if (uaa.temp_dev == NULL) continue; if (device_delete_child(udev->parent_dev, uaa.temp_dev)) DPRINTFN(0, "device delete child failed\n"); uaa.temp_dev = NULL; } done: if (do_unlock) usbd_enum_unlock(udev); return (0); } /*------------------------------------------------------------------------* * usb_suspend_resume_sub * * This function is called when the suspend or resume methods should * be executed on an USB device. *------------------------------------------------------------------------*/ static void usb_suspend_resume_sub(struct usb_device *udev, device_t dev, uint8_t do_suspend) { int err; if (dev == NULL) { return; } if (!device_is_attached(dev)) { return; } if (do_suspend) { err = DEVICE_SUSPEND(dev); } else { err = DEVICE_RESUME(dev); } if (err) { device_printf(dev, "%s failed\n", do_suspend ? "Suspend" : "Resume"); } } /*------------------------------------------------------------------------* * usb_suspend_resume * * The following function will suspend or resume the USB device. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usb_suspend_resume(struct usb_device *udev, uint8_t do_suspend) { struct usb_interface *iface; uint8_t i; if (udev == NULL) { /* nothing to do */ return (0); } DPRINTFN(4, "udev=%p do_suspend=%d\n", udev, do_suspend); sx_assert(&udev->sr_sx, SA_LOCKED); USB_BUS_LOCK(udev->bus); /* filter the suspend events */ if (udev->flags.peer_suspended == do_suspend) { USB_BUS_UNLOCK(udev->bus); /* nothing to do */ return (0); } udev->flags.peer_suspended = do_suspend; USB_BUS_UNLOCK(udev->bus); /* do the suspend or resume */ for (i = 0; i != USB_IFACE_MAX; i++) { iface = usbd_get_iface(udev, i); if (iface == NULL) { /* looks like the end of the USB interfaces */ break; } usb_suspend_resume_sub(udev, iface->subdev, do_suspend); } return (0); } /*------------------------------------------------------------------------* * usbd_clear_stall_proc * * This function performs generic USB clear stall operations. *------------------------------------------------------------------------*/ static void usbd_clear_stall_proc(struct usb_proc_msg *_pm) { struct usb_udev_msg *pm = (void *)_pm; struct usb_device *udev = pm->udev; /* Change lock */ USB_BUS_UNLOCK(udev->bus); mtx_lock(&udev->device_mtx); /* Start clear stall callback */ usbd_transfer_start(udev->ctrl_xfer[1]); /* Change lock */ mtx_unlock(&udev->device_mtx); USB_BUS_LOCK(udev->bus); } /*------------------------------------------------------------------------* * usb_alloc_device * * This function allocates a new USB device. This function is called * when a new device has been put in the powered state, but not yet in * the addressed state. Get initial descriptor, set the address, get * full descriptor and get strings. * * Return values: * 0: Failure * Else: Success *------------------------------------------------------------------------*/ struct usb_device * usb_alloc_device(device_t parent_dev, struct usb_bus *bus, struct usb_device *parent_hub, uint8_t depth, uint8_t port_index, uint8_t port_no, enum usb_dev_speed speed, enum usb_hc_mode mode) { struct usb_attach_arg uaa; struct usb_device *udev; struct usb_device *adev; struct usb_device *hub; uint8_t *scratch_ptr; usb_error_t err; uint8_t device_index; uint8_t config_index; uint8_t config_quirk; uint8_t set_config_failed; uint8_t do_unlock; DPRINTF("parent_dev=%p, bus=%p, parent_hub=%p, depth=%u, " "port_index=%u, port_no=%u, speed=%u, usb_mode=%u\n", parent_dev, bus, parent_hub, depth, port_index, port_no, speed, mode); /* * Find an unused device index. In USB Host mode this is the * same as the device address. * * Device index zero is not used and device index 1 should * always be the root hub. */ for (device_index = USB_ROOT_HUB_ADDR; (device_index != bus->devices_max) && (bus->devices[device_index] != NULL); device_index++) /* nop */; if (device_index == bus->devices_max) { device_printf(bus->bdev, "No free USB device index for new device\n"); return (NULL); } if (depth > 0x10) { device_printf(bus->bdev, "Invalid device depth\n"); return (NULL); } udev = malloc(sizeof(*udev), M_USB, M_WAITOK | M_ZERO); if (udev == NULL) { return (NULL); } /* initialise our SX-lock */ sx_init_flags(&udev->enum_sx, "USB config SX lock", SX_DUPOK); sx_init_flags(&udev->sr_sx, "USB suspend and resume SX lock", SX_NOWITNESS); + sx_init_flags(&udev->ctrl_sx, "USB control transfer SX lock", SX_DUPOK); cv_init(&udev->ctrlreq_cv, "WCTRL"); cv_init(&udev->ref_cv, "UGONE"); /* initialise our mutex */ mtx_init(&udev->device_mtx, "USB device mutex", NULL, MTX_DEF); /* initialise generic clear stall */ udev->cs_msg[0].hdr.pm_callback = &usbd_clear_stall_proc; udev->cs_msg[0].udev = udev; udev->cs_msg[1].hdr.pm_callback = &usbd_clear_stall_proc; udev->cs_msg[1].udev = udev; /* initialise some USB device fields */ udev->parent_hub = parent_hub; udev->parent_dev = parent_dev; udev->port_index = port_index; udev->port_no = port_no; udev->depth = depth; udev->bus = bus; udev->address = USB_START_ADDR; /* default value */ udev->plugtime = (usb_ticks_t)ticks; /* * We need to force the power mode to "on" because there are plenty * of USB devices out there that do not work very well with * automatic suspend and resume! */ udev->power_mode = usbd_filter_power_mode(udev, USB_POWER_MODE_ON); udev->pwr_save.last_xfer_time = ticks; /* we are not ready yet */ udev->refcount = 1; /* set up default endpoint descriptor */ udev->ctrl_ep_desc.bLength = sizeof(udev->ctrl_ep_desc); udev->ctrl_ep_desc.bDescriptorType = UDESC_ENDPOINT; udev->ctrl_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT; udev->ctrl_ep_desc.bmAttributes = UE_CONTROL; udev->ctrl_ep_desc.wMaxPacketSize[0] = USB_MAX_IPACKET; udev->ctrl_ep_desc.wMaxPacketSize[1] = 0; udev->ctrl_ep_desc.bInterval = 0; /* set up default endpoint companion descriptor */ udev->ctrl_ep_comp_desc.bLength = sizeof(udev->ctrl_ep_comp_desc); udev->ctrl_ep_comp_desc.bDescriptorType = UDESC_ENDPOINT_SS_COMP; udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; udev->speed = speed; udev->flags.usb_mode = mode; /* search for our High Speed USB HUB, if any */ adev = udev; hub = udev->parent_hub; while (hub) { if (hub->speed == USB_SPEED_HIGH) { udev->hs_hub_addr = hub->address; udev->parent_hs_hub = hub; udev->hs_port_no = adev->port_no; break; } adev = hub; hub = hub->parent_hub; } /* init the default endpoint */ usb_init_endpoint(udev, 0, &udev->ctrl_ep_desc, &udev->ctrl_ep_comp_desc, &udev->ctrl_ep); /* set device index */ udev->device_index = device_index; #if USB_HAVE_UGEN /* Create ugen name */ snprintf(udev->ugen_name, sizeof(udev->ugen_name), USB_GENERIC_NAME "%u.%u", device_get_unit(bus->bdev), device_index); LIST_INIT(&udev->pd_list); /* Create the control endpoint device */ udev->ctrl_dev = usb_make_dev(udev, NULL, 0, 0, FREAD|FWRITE, UID_ROOT, GID_OPERATOR, 0600); /* Create a link from /dev/ugenX.X to the default endpoint */ if (udev->ctrl_dev != NULL) make_dev_alias(udev->ctrl_dev->cdev, "%s", udev->ugen_name); #endif /* Initialise device */ if (bus->methods->device_init != NULL) { err = (bus->methods->device_init) (udev); if (err != 0) { DPRINTFN(0, "device init %d failed " "(%s, ignored)\n", device_index, usbd_errstr(err)); goto done; } } /* set powered device state after device init is complete */ usb_set_device_state(udev, USB_STATE_POWERED); if (udev->flags.usb_mode == USB_MODE_HOST) { err = usbd_req_set_address(udev, NULL, device_index); /* * This is the new USB device address from now on, if * the set address request didn't set it already. */ if (udev->address == USB_START_ADDR) udev->address = device_index; /* * We ignore any set-address errors, hence there are * buggy USB devices out there that actually receive * the SETUP PID, but manage to set the address before * the STATUS stage is ACK'ed. If the device responds * to the subsequent get-descriptor at the new * address, then we know that the set-address command * was successful. */ if (err) { DPRINTFN(0, "set address %d failed " "(%s, ignored)\n", udev->address, usbd_errstr(err)); } } else { /* We are not self powered */ udev->flags.self_powered = 0; /* Set unconfigured state */ udev->curr_config_no = USB_UNCONFIG_NO; udev->curr_config_index = USB_UNCONFIG_INDEX; /* Setup USB descriptors */ err = (usb_temp_setup_by_index_p) (udev, usb_template); if (err) { DPRINTFN(0, "setting up USB template failed - " "usb_template(4) not loaded?\n"); goto done; } } usb_set_device_state(udev, USB_STATE_ADDRESSED); /* setup the device descriptor and the initial "wMaxPacketSize" */ err = usbd_setup_device_desc(udev, NULL); if (err != 0) { /* try to enumerate two more times */ err = usbd_req_re_enumerate(udev, NULL); if (err != 0) { err = usbd_req_re_enumerate(udev, NULL); if (err != 0) { goto done; } } } /* * Setup temporary USB attach args so that we can figure out some * basic quirks for this device. */ usb_init_attach_arg(udev, &uaa); if (usb_test_quirk(&uaa, UQ_BUS_POWERED)) { udev->flags.uq_bus_powered = 1; } if (usb_test_quirk(&uaa, UQ_NO_STRINGS)) { udev->flags.no_strings = 1; } /* * Workaround for buggy USB devices. * * It appears that some string-less USB chips will crash and * disappear if any attempts are made to read any string * descriptors. * * Try to detect such chips by checking the strings in the USB * device descriptor. If no strings are present there we * simply disable all USB strings. */ /* Protect scratch area */ - do_unlock = usbd_enum_lock(udev); + do_unlock = usbd_ctrl_lock(udev); scratch_ptr = udev->scratch.data; if (udev->flags.no_strings) { err = USB_ERR_INVAL; } else if (udev->ddesc.iManufacturer || udev->ddesc.iProduct || udev->ddesc.iSerialNumber) { /* read out the language ID string */ err = usbd_req_get_string_desc(udev, NULL, (char *)scratch_ptr, 4, 0, USB_LANGUAGE_TABLE); } else { err = USB_ERR_INVAL; } if (err || (scratch_ptr[0] < 4)) { udev->flags.no_strings = 1; } else { uint16_t langid; uint16_t pref; uint16_t mask; uint8_t x; /* load preferred value and mask */ pref = usb_lang_id; mask = usb_lang_mask; /* align length correctly */ scratch_ptr[0] &= ~1U; /* fix compiler warning */ langid = 0; /* search for preferred language */ for (x = 2; (x < scratch_ptr[0]); x += 2) { langid = UGETW(scratch_ptr + x); if ((langid & mask) == pref) break; } if (x >= scratch_ptr[0]) { /* pick the first language as the default */ DPRINTFN(1, "Using first language\n"); langid = UGETW(scratch_ptr + 2); } DPRINTFN(1, "Language selected: 0x%04x\n", langid); udev->langid = langid; } if (do_unlock) - usbd_enum_unlock(udev); + usbd_ctrl_unlock(udev); /* assume 100mA bus powered for now. Changed when configured. */ udev->power = USB_MIN_POWER; /* fetch the vendor and product strings from the device */ usbd_set_device_strings(udev); if (udev->flags.usb_mode == USB_MODE_DEVICE) { /* USB device mode setup is complete */ err = 0; goto config_done; } /* * Most USB devices should attach to config index 0 by * default */ if (usb_test_quirk(&uaa, UQ_CFG_INDEX_0)) { config_index = 0; config_quirk = 1; } else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_1)) { config_index = 1; config_quirk = 1; } else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_2)) { config_index = 2; config_quirk = 1; } else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_3)) { config_index = 3; config_quirk = 1; } else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_4)) { config_index = 4; config_quirk = 1; } else { config_index = 0; config_quirk = 0; } set_config_failed = 0; repeat_set_config: DPRINTF("setting config %u\n", config_index); /* get the USB device configured */ err = usbd_set_config_index(udev, config_index); if (err) { if (udev->ddesc.bNumConfigurations != 0) { if (!set_config_failed) { set_config_failed = 1; /* XXX try to re-enumerate the device */ err = usbd_req_re_enumerate(udev, NULL); if (err == 0) goto repeat_set_config; } DPRINTFN(0, "Failure selecting configuration index %u:" "%s, port %u, addr %u (ignored)\n", config_index, usbd_errstr(err), udev->port_no, udev->address); } /* * Some USB devices do not have any configurations. Ignore any * set config failures! */ err = 0; goto config_done; } if (!config_quirk && config_index + 1 < udev->ddesc.bNumConfigurations) { if ((udev->cdesc->bNumInterface < 2) && usbd_get_no_descriptors(udev->cdesc, UDESC_ENDPOINT) == 0) { DPRINTFN(0, "Found no endpoints, trying next config\n"); config_index++; goto repeat_set_config; } #if USB_HAVE_MSCTEST if (config_index == 0) { /* * Try to figure out if we have an * auto-install disk there: */ if (usb_iface_is_cdrom(udev, 0)) { DPRINTFN(0, "Found possible auto-install " "disk (trying next config)\n"); config_index++; goto repeat_set_config; } } #endif } #if USB_HAVE_MSCTEST if (set_config_failed == 0 && config_index == 0 && usb_test_quirk(&uaa, UQ_MSC_NO_SYNC_CACHE) == 0 && usb_test_quirk(&uaa, UQ_MSC_NO_GETMAXLUN) == 0) { /* * Try to figure out if there are any MSC quirks we * should apply automatically: */ err = usb_msc_auto_quirk(udev, 0); if (err != 0) { set_config_failed = 1; goto repeat_set_config; } } #endif config_done: DPRINTF("new dev (addr %d), udev=%p, parent_hub=%p\n", udev->address, udev, udev->parent_hub); /* register our device - we are ready */ usb_bus_port_set_device(bus, parent_hub ? parent_hub->hub->ports + port_index : NULL, udev, device_index); #if USB_HAVE_UGEN /* Symlink the ugen device name */ udev->ugen_symlink = usb_alloc_symlink(udev->ugen_name); /* Announce device */ printf("%s: <%s> at %s\n", udev->ugen_name, usb_get_manufacturer(udev), device_get_nameunit(udev->bus->bdev)); #endif #if USB_HAVE_DEVCTL usb_notify_addq("ATTACH", udev); #endif done: if (err) { /* * Free USB device and all subdevices, if any. */ usb_free_device(udev, 0); udev = NULL; } return (udev); } #if USB_HAVE_UGEN struct usb_fs_privdata * usb_make_dev(struct usb_device *udev, const char *devname, int ep, int fi, int rwmode, uid_t uid, gid_t gid, int mode) { struct usb_fs_privdata* pd; struct make_dev_args args; char buffer[32]; /* Store information to locate ourselves again later */ pd = malloc(sizeof(struct usb_fs_privdata), M_USBDEV, M_WAITOK | M_ZERO); pd->bus_index = device_get_unit(udev->bus->bdev); pd->dev_index = udev->device_index; pd->ep_addr = ep; pd->fifo_index = fi; pd->mode = rwmode; /* Now, create the device itself */ if (devname == NULL) { devname = buffer; snprintf(buffer, sizeof(buffer), USB_DEVICE_DIR "/%u.%u.%u", pd->bus_index, pd->dev_index, pd->ep_addr); } /* Setup arguments for make_dev_s() */ make_dev_args_init(&args); args.mda_devsw = &usb_devsw; args.mda_uid = uid; args.mda_gid = gid; args.mda_mode = mode; args.mda_si_drv1 = pd; if (make_dev_s(&args, &pd->cdev, "%s", devname) != 0) { DPRINTFN(0, "Failed to create device %s\n", devname); free(pd, M_USBDEV); return (NULL); } return (pd); } void usb_destroy_dev_sync(struct usb_fs_privdata *pd) { DPRINTFN(1, "Destroying device at ugen%d.%d\n", pd->bus_index, pd->dev_index); /* * Destroy character device synchronously. After this * all system calls are returned. Can block. */ destroy_dev(pd->cdev); free(pd, M_USBDEV); } void usb_destroy_dev(struct usb_fs_privdata *pd) { struct usb_bus *bus; if (pd == NULL) return; mtx_lock(&usb_ref_lock); bus = devclass_get_softc(usb_devclass_ptr, pd->bus_index); mtx_unlock(&usb_ref_lock); if (bus == NULL) { usb_destroy_dev_sync(pd); return; } /* make sure we can re-use the device name */ delist_dev(pd->cdev); USB_BUS_LOCK(bus); LIST_INSERT_HEAD(&bus->pd_cleanup_list, pd, pd_next); /* get cleanup going */ usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), &bus->cleanup_msg[0], &bus->cleanup_msg[1]); USB_BUS_UNLOCK(bus); } static void usb_cdev_create(struct usb_device *udev) { struct usb_config_descriptor *cd; struct usb_endpoint_descriptor *ed; struct usb_descriptor *desc; struct usb_fs_privdata* pd; int inmode, outmode, inmask, outmask, mode; uint8_t ep; KASSERT(LIST_FIRST(&udev->pd_list) == NULL, ("stale cdev entries")); DPRINTFN(2, "Creating device nodes\n"); if (usbd_get_mode(udev) == USB_MODE_DEVICE) { inmode = FWRITE; outmode = FREAD; } else { /* USB_MODE_HOST */ inmode = FREAD; outmode = FWRITE; } inmask = 0; outmask = 0; desc = NULL; /* * Collect all used endpoint numbers instead of just * generating 16 static endpoints. */ cd = usbd_get_config_descriptor(udev); while ((desc = usb_desc_foreach(cd, desc))) { /* filter out all endpoint descriptors */ if ((desc->bDescriptorType == UDESC_ENDPOINT) && (desc->bLength >= sizeof(*ed))) { ed = (struct usb_endpoint_descriptor *)desc; /* update masks */ ep = ed->bEndpointAddress; if (UE_GET_DIR(ep) == UE_DIR_OUT) outmask |= 1 << UE_GET_ADDR(ep); else inmask |= 1 << UE_GET_ADDR(ep); } } /* Create all available endpoints except EP0 */ for (ep = 1; ep < 16; ep++) { mode = (inmask & (1 << ep)) ? inmode : 0; mode |= (outmask & (1 << ep)) ? outmode : 0; if (mode == 0) continue; /* no IN or OUT endpoint */ pd = usb_make_dev(udev, NULL, ep, 0, mode, UID_ROOT, GID_OPERATOR, 0600); if (pd != NULL) LIST_INSERT_HEAD(&udev->pd_list, pd, pd_next); } } static void usb_cdev_free(struct usb_device *udev) { struct usb_fs_privdata* pd; DPRINTFN(2, "Freeing device nodes\n"); while ((pd = LIST_FIRST(&udev->pd_list)) != NULL) { KASSERT(pd->cdev->si_drv1 == pd, ("privdata corrupt")); LIST_REMOVE(pd, pd_next); usb_destroy_dev(pd); } } #endif /*------------------------------------------------------------------------* * usb_free_device * * This function is NULL safe and will free an USB device and its * children devices, if any. * * Flag values: Reserved, set to zero. *------------------------------------------------------------------------*/ void usb_free_device(struct usb_device *udev, uint8_t flag) { struct usb_bus *bus; if (udev == NULL) return; /* already freed */ DPRINTFN(4, "udev=%p port=%d\n", udev, udev->port_no); bus = udev->bus; /* set DETACHED state to prevent any further references */ usb_set_device_state(udev, USB_STATE_DETACHED); #if USB_HAVE_DEVCTL usb_notify_addq("DETACH", udev); #endif #if USB_HAVE_UGEN if (!rebooting) { printf("%s: <%s> at %s (disconnected)\n", udev->ugen_name, usb_get_manufacturer(udev), device_get_nameunit(bus->bdev)); } /* Destroy UGEN symlink, if any */ if (udev->ugen_symlink) { usb_free_symlink(udev->ugen_symlink); udev->ugen_symlink = NULL; } usb_destroy_dev(udev->ctrl_dev); #endif if (udev->flags.usb_mode == USB_MODE_DEVICE) { /* stop receiving any control transfers (Device Side Mode) */ usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); } /* the following will get the device unconfigured in software */ usb_unconfigure(udev, USB_UNCFG_FLAG_FREE_EP0); /* final device unregister after all character devices are closed */ usb_bus_port_set_device(bus, udev->parent_hub ? udev->parent_hub->hub->ports + udev->port_index : NULL, NULL, USB_ROOT_HUB_ADDR); /* unsetup any leftover default USB transfers */ usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); /* template unsetup, if any */ (usb_temp_unsetup_p) (udev); /* * Make sure that our clear-stall messages are not queued * anywhere: */ USB_BUS_LOCK(udev->bus); usb_proc_mwait(USB_BUS_CS_PROC(udev->bus), &udev->cs_msg[0], &udev->cs_msg[1]); USB_BUS_UNLOCK(udev->bus); /* wait for all references to go away */ usb_wait_pending_refs(udev); sx_destroy(&udev->enum_sx); sx_destroy(&udev->sr_sx); + sx_destroy(&udev->ctrl_sx); cv_destroy(&udev->ctrlreq_cv); cv_destroy(&udev->ref_cv); mtx_destroy(&udev->device_mtx); #if USB_HAVE_UGEN KASSERT(LIST_FIRST(&udev->pd_list) == NULL, ("leaked cdev entries")); #endif /* Uninitialise device */ if (bus->methods->device_uninit != NULL) (bus->methods->device_uninit) (udev); /* free device */ free(udev->serial, M_USB); free(udev->manufacturer, M_USB); free(udev->product, M_USB); free(udev, M_USB); } /*------------------------------------------------------------------------* * usbd_get_iface * * This function is the safe way to get the USB interface structure * pointer by interface index. * * Return values: * NULL: Interface not present. * Else: Pointer to USB interface structure. *------------------------------------------------------------------------*/ struct usb_interface * usbd_get_iface(struct usb_device *udev, uint8_t iface_index) { struct usb_interface *iface = udev->ifaces + iface_index; if (iface_index >= udev->ifaces_max) return (NULL); return (iface); } /*------------------------------------------------------------------------* * usbd_find_descriptor * * This function will lookup the first descriptor that matches the * criteria given by the arguments "type" and "subtype". Descriptors * will only be searched within the interface having the index * "iface_index". If the "id" argument points to an USB descriptor, * it will be skipped before the search is started. This allows * searching for multiple descriptors using the same criteria. Else * the search is started after the interface descriptor. * * Return values: * NULL: End of descriptors * Else: A descriptor matching the criteria *------------------------------------------------------------------------*/ void * usbd_find_descriptor(struct usb_device *udev, void *id, uint8_t iface_index, uint8_t type, uint8_t type_mask, uint8_t subtype, uint8_t subtype_mask) { struct usb_descriptor *desc; struct usb_config_descriptor *cd; struct usb_interface *iface; cd = usbd_get_config_descriptor(udev); if (cd == NULL) { return (NULL); } if (id == NULL) { iface = usbd_get_iface(udev, iface_index); if (iface == NULL) { return (NULL); } id = usbd_get_interface_descriptor(iface); if (id == NULL) { return (NULL); } } desc = (void *)id; while ((desc = usb_desc_foreach(cd, desc))) { if (desc->bDescriptorType == UDESC_INTERFACE) { break; } if (((desc->bDescriptorType & type_mask) == type) && ((desc->bDescriptorSubtype & subtype_mask) == subtype)) { return (desc); } } return (NULL); } /*------------------------------------------------------------------------* * usb_devinfo * * This function will dump information from the device descriptor * belonging to the USB device pointed to by "udev", to the string * pointed to by "dst_ptr" having a maximum length of "dst_len" bytes * including the terminating zero. *------------------------------------------------------------------------*/ void usb_devinfo(struct usb_device *udev, char *dst_ptr, uint16_t dst_len) { struct usb_device_descriptor *udd = &udev->ddesc; uint16_t bcdDevice; uint16_t bcdUSB; bcdUSB = UGETW(udd->bcdUSB); bcdDevice = UGETW(udd->bcdDevice); if (udd->bDeviceClass != 0xFF) { snprintf(dst_ptr, dst_len, "%s %s, class %d/%d, rev %x.%02x/" "%x.%02x, addr %d", usb_get_manufacturer(udev), usb_get_product(udev), udd->bDeviceClass, udd->bDeviceSubClass, (bcdUSB >> 8), bcdUSB & 0xFF, (bcdDevice >> 8), bcdDevice & 0xFF, udev->address); } else { snprintf(dst_ptr, dst_len, "%s %s, rev %x.%02x/" "%x.%02x, addr %d", usb_get_manufacturer(udev), usb_get_product(udev), (bcdUSB >> 8), bcdUSB & 0xFF, (bcdDevice >> 8), bcdDevice & 0xFF, udev->address); } } #ifdef USB_VERBOSE /* * Descriptions of of known vendors and devices ("products"). */ struct usb_knowndev { uint16_t vendor; uint16_t product; uint32_t flags; const char *vendorname; const char *productname; }; #define USB_KNOWNDEV_NOPROD 0x01 /* match on vendor only */ #include "usbdevs.h" #include "usbdevs_data.h" #endif /* USB_VERBOSE */ static void usbd_set_device_strings(struct usb_device *udev) { struct usb_device_descriptor *udd = &udev->ddesc; #ifdef USB_VERBOSE const struct usb_knowndev *kdp; #endif char *temp_ptr; size_t temp_size; uint16_t vendor_id; uint16_t product_id; uint8_t do_unlock; /* Protect scratch area */ - do_unlock = usbd_enum_lock(udev); + do_unlock = usbd_ctrl_lock(udev); temp_ptr = (char *)udev->scratch.data; temp_size = sizeof(udev->scratch.data); vendor_id = UGETW(udd->idVendor); product_id = UGETW(udd->idProduct); /* get serial number string */ usbd_req_get_string_any(udev, NULL, temp_ptr, temp_size, udev->ddesc.iSerialNumber); udev->serial = strdup(temp_ptr, M_USB); /* get manufacturer string */ usbd_req_get_string_any(udev, NULL, temp_ptr, temp_size, udev->ddesc.iManufacturer); usb_trim_spaces(temp_ptr); if (temp_ptr[0] != '\0') udev->manufacturer = strdup(temp_ptr, M_USB); /* get product string */ usbd_req_get_string_any(udev, NULL, temp_ptr, temp_size, udev->ddesc.iProduct); usb_trim_spaces(temp_ptr); if (temp_ptr[0] != '\0') udev->product = strdup(temp_ptr, M_USB); #ifdef USB_VERBOSE if (udev->manufacturer == NULL || udev->product == NULL) { for (kdp = usb_knowndevs; kdp->vendorname != NULL; kdp++) { if (kdp->vendor == vendor_id && (kdp->product == product_id || (kdp->flags & USB_KNOWNDEV_NOPROD) != 0)) break; } if (kdp->vendorname != NULL) { /* XXX should use pointer to knowndevs string */ if (udev->manufacturer == NULL) { udev->manufacturer = strdup(kdp->vendorname, M_USB); } if (udev->product == NULL && (kdp->flags & USB_KNOWNDEV_NOPROD) == 0) { udev->product = strdup(kdp->productname, M_USB); } } } #endif /* Provide default strings if none were found */ if (udev->manufacturer == NULL) { snprintf(temp_ptr, temp_size, "vendor 0x%04x", vendor_id); udev->manufacturer = strdup(temp_ptr, M_USB); } if (udev->product == NULL) { snprintf(temp_ptr, temp_size, "product 0x%04x", product_id); udev->product = strdup(temp_ptr, M_USB); } if (do_unlock) - usbd_enum_unlock(udev); + usbd_ctrl_unlock(udev); } /* * Returns: * See: USB_MODE_XXX */ enum usb_hc_mode usbd_get_mode(struct usb_device *udev) { return (udev->flags.usb_mode); } /* * Returns: * See: USB_SPEED_XXX */ enum usb_dev_speed usbd_get_speed(struct usb_device *udev) { return (udev->speed); } uint32_t usbd_get_isoc_fps(struct usb_device *udev) { ; /* indent fix */ switch (udev->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: return (1000); default: return (8000); } } struct usb_device_descriptor * usbd_get_device_descriptor(struct usb_device *udev) { if (udev == NULL) return (NULL); /* be NULL safe */ return (&udev->ddesc); } struct usb_config_descriptor * usbd_get_config_descriptor(struct usb_device *udev) { if (udev == NULL) return (NULL); /* be NULL safe */ return (udev->cdesc); } /*------------------------------------------------------------------------* * usb_test_quirk - test a device for a given quirk * * Return values: * 0: The USB device does not have the given quirk. * Else: The USB device has the given quirk. *------------------------------------------------------------------------*/ uint8_t usb_test_quirk(const struct usb_attach_arg *uaa, uint16_t quirk) { uint8_t found; uint8_t x; if (quirk == UQ_NONE) return (0); /* search the automatic per device quirks first */ for (x = 0; x != USB_MAX_AUTO_QUIRK; x++) { if (uaa->device->autoQuirk[x] == quirk) return (1); } /* search global quirk table, if any */ found = (usb_test_quirk_p) (&uaa->info, quirk); return (found); } struct usb_interface_descriptor * usbd_get_interface_descriptor(struct usb_interface *iface) { if (iface == NULL) return (NULL); /* be NULL safe */ return (iface->idesc); } uint8_t usbd_get_interface_altindex(struct usb_interface *iface) { return (iface->alt_index); } uint8_t usbd_get_bus_index(struct usb_device *udev) { return ((uint8_t)device_get_unit(udev->bus->bdev)); } uint8_t usbd_get_device_index(struct usb_device *udev) { return (udev->device_index); } #if USB_HAVE_DEVCTL static void usb_notify_addq(const char *type, struct usb_device *udev) { struct usb_interface *iface; struct sbuf *sb; int i; /* announce the device */ sb = sbuf_new_auto(); sbuf_printf(sb, #if USB_HAVE_UGEN "ugen=%s " "cdev=%s " #endif "vendor=0x%04x " "product=0x%04x " "devclass=0x%02x " "devsubclass=0x%02x " "sernum=\"%s\" " "release=0x%04x " "mode=%s " "port=%u " #if USB_HAVE_UGEN "parent=%s" #endif "", #if USB_HAVE_UGEN udev->ugen_name, udev->ugen_name, #endif UGETW(udev->ddesc.idVendor), UGETW(udev->ddesc.idProduct), udev->ddesc.bDeviceClass, udev->ddesc.bDeviceSubClass, usb_get_serial(udev), UGETW(udev->ddesc.bcdDevice), (udev->flags.usb_mode == USB_MODE_HOST) ? "host" : "device", udev->port_no #if USB_HAVE_UGEN , udev->parent_hub != NULL ? udev->parent_hub->ugen_name : device_get_nameunit(device_get_parent(udev->bus->bdev)) #endif ); sbuf_finish(sb); devctl_notify("USB", "DEVICE", type, sbuf_data(sb)); sbuf_delete(sb); /* announce each interface */ for (i = 0; i < USB_IFACE_MAX; i++) { iface = usbd_get_iface(udev, i); if (iface == NULL) break; /* end of interfaces */ if (iface->idesc == NULL) continue; /* no interface descriptor */ sb = sbuf_new_auto(); sbuf_printf(sb, #if USB_HAVE_UGEN "ugen=%s " "cdev=%s " #endif "vendor=0x%04x " "product=0x%04x " "devclass=0x%02x " "devsubclass=0x%02x " "sernum=\"%s\" " "release=0x%04x " "mode=%s " "interface=%d " "endpoints=%d " "intclass=0x%02x " "intsubclass=0x%02x " "intprotocol=0x%02x", #if USB_HAVE_UGEN udev->ugen_name, udev->ugen_name, #endif UGETW(udev->ddesc.idVendor), UGETW(udev->ddesc.idProduct), udev->ddesc.bDeviceClass, udev->ddesc.bDeviceSubClass, usb_get_serial(udev), UGETW(udev->ddesc.bcdDevice), (udev->flags.usb_mode == USB_MODE_HOST) ? "host" : "device", iface->idesc->bInterfaceNumber, iface->idesc->bNumEndpoints, iface->idesc->bInterfaceClass, iface->idesc->bInterfaceSubClass, iface->idesc->bInterfaceProtocol); sbuf_finish(sb); devctl_notify("USB", "INTERFACE", type, sbuf_data(sb)); sbuf_delete(sb); } } #endif #if USB_HAVE_UGEN /*------------------------------------------------------------------------* * usb_fifo_free_wrap * * This function will free the FIFOs. * * Description of "flag" argument: If the USB_UNCFG_FLAG_FREE_EP0 flag * is set and "iface_index" is set to "USB_IFACE_INDEX_ANY", we free * all FIFOs. If the USB_UNCFG_FLAG_FREE_EP0 flag is not set and * "iface_index" is set to "USB_IFACE_INDEX_ANY", we free all non * control endpoint FIFOs. If "iface_index" is not set to * "USB_IFACE_INDEX_ANY" the flag has no effect. *------------------------------------------------------------------------*/ static void usb_fifo_free_wrap(struct usb_device *udev, uint8_t iface_index, uint8_t flag) { struct usb_fifo *f; uint16_t i; /* * Free any USB FIFOs on the given interface: */ for (i = 0; i != USB_FIFO_MAX; i++) { f = udev->fifo[i]; if (f == NULL) { continue; } /* Check if the interface index matches */ if (iface_index == f->iface_index) { if (f->methods != &usb_ugen_methods) { /* * Don't free any non-generic FIFOs in * this case. */ continue; } if ((f->dev_ep_index == 0) && (f->fs_xfer == NULL)) { /* no need to free this FIFO */ continue; } } else if (iface_index == USB_IFACE_INDEX_ANY) { if ((f->methods == &usb_ugen_methods) && (f->dev_ep_index == 0) && (!(flag & USB_UNCFG_FLAG_FREE_EP0)) && (f->fs_xfer == NULL)) { /* no need to free this FIFO */ continue; } } else { /* no need to free this FIFO */ continue; } /* free this FIFO */ usb_fifo_free(f); } } #endif /*------------------------------------------------------------------------* * usb_peer_can_wakeup * * Return values: * 0: Peer cannot do resume signalling. * Else: Peer can do resume signalling. *------------------------------------------------------------------------*/ uint8_t usb_peer_can_wakeup(struct usb_device *udev) { const struct usb_config_descriptor *cdp; cdp = udev->cdesc; if ((cdp != NULL) && (udev->flags.usb_mode == USB_MODE_HOST)) { return (cdp->bmAttributes & UC_REMOTE_WAKEUP); } return (0); /* not supported */ } void usb_set_device_state(struct usb_device *udev, enum usb_dev_state state) { KASSERT(state < USB_STATE_MAX, ("invalid udev state")); DPRINTF("udev %p state %s -> %s\n", udev, usb_statestr(udev->state), usb_statestr(state)); #if USB_HAVE_UGEN mtx_lock(&usb_ref_lock); #endif udev->state = state; #if USB_HAVE_UGEN mtx_unlock(&usb_ref_lock); #endif if (udev->bus->methods->device_state_change != NULL) (udev->bus->methods->device_state_change) (udev); } enum usb_dev_state usb_get_device_state(struct usb_device *udev) { if (udev == NULL) return (USB_STATE_DETACHED); return (udev->state); } uint8_t usbd_device_attached(struct usb_device *udev) { return (udev->state > USB_STATE_DETACHED); } /* * The following function locks enumerating the given USB device. If * the lock is already grabbed this function returns zero. Else a * a value of one is returned. */ uint8_t usbd_enum_lock(struct usb_device *udev) { if (sx_xlocked(&udev->enum_sx)) return (0); sx_xlock(&udev->enum_sx); sx_xlock(&udev->sr_sx); /* * NEWBUS LOCK NOTE: We should check if any parent SX locks * are locked before locking Giant. Else the lock can be * locked multiple times. */ mtx_lock(&Giant); return (1); } #if USB_HAVE_UGEN /* * This function is the same like usbd_enum_lock() except a value of * 255 is returned when a signal is pending: */ uint8_t usbd_enum_lock_sig(struct usb_device *udev) { if (sx_xlocked(&udev->enum_sx)) return (0); if (sx_xlock_sig(&udev->enum_sx)) return (255); if (sx_xlock_sig(&udev->sr_sx)) { sx_xunlock(&udev->enum_sx); return (255); } mtx_lock(&Giant); return (1); } #endif /* The following function unlocks enumerating the given USB device. */ void usbd_enum_unlock(struct usb_device *udev) { mtx_unlock(&Giant); sx_xunlock(&udev->enum_sx); sx_xunlock(&udev->sr_sx); } /* The following function locks suspend and resume. */ void usbd_sr_lock(struct usb_device *udev) { sx_xlock(&udev->sr_sx); /* * NEWBUS LOCK NOTE: We should check if any parent SX locks * are locked before locking Giant. Else the lock can be * locked multiple times. */ mtx_lock(&Giant); } /* The following function unlocks suspend and resume. */ void usbd_sr_unlock(struct usb_device *udev) { mtx_unlock(&Giant); sx_xunlock(&udev->sr_sx); } /* * The following function checks the enumerating lock for the given * USB device. */ uint8_t usbd_enum_is_locked(struct usb_device *udev) { return (sx_xlocked(&udev->enum_sx)); +} + +/* + * The following function is used to serialize access to USB control + * transfers and the USB scratch area. If the lock is already grabbed + * this function returns zero. Else a value of one is returned. + */ +uint8_t +usbd_ctrl_lock(struct usb_device *udev) +{ + if (sx_xlocked(&udev->ctrl_sx)) + return (0); + sx_xlock(&udev->ctrl_sx); + + /* + * We need to allow suspend and resume at this point, else the + * control transfer will timeout if the device is suspended! + */ + if (usbd_enum_is_locked(udev)) + usbd_sr_unlock(udev); + return (1); +} + +void +usbd_ctrl_unlock(struct usb_device *udev) +{ + sx_xunlock(&udev->ctrl_sx); + + /* + * Restore the suspend and resume lock after we have unlocked + * the USB control transfer lock to avoid LOR: + */ + if (usbd_enum_is_locked(udev)) + usbd_sr_lock(udev); } /* * The following function is used to set the per-interface specific * plug and play information. The string referred to by the pnpinfo * argument can safely be freed after calling this function. The * pnpinfo of an interface will be reset at device detach or when * passing a NULL argument to this function. This function * returns zero on success, else a USB_ERR_XXX failure code. */ usb_error_t usbd_set_pnpinfo(struct usb_device *udev, uint8_t iface_index, const char *pnpinfo) { struct usb_interface *iface; iface = usbd_get_iface(udev, iface_index); if (iface == NULL) return (USB_ERR_INVAL); if (iface->pnpinfo != NULL) { free(iface->pnpinfo, M_USBDEV); iface->pnpinfo = NULL; } if (pnpinfo == NULL || pnpinfo[0] == 0) return (0); /* success */ iface->pnpinfo = strdup(pnpinfo, M_USBDEV); if (iface->pnpinfo == NULL) return (USB_ERR_NOMEM); return (0); /* success */ } usb_error_t usbd_add_dynamic_quirk(struct usb_device *udev, uint16_t quirk) { uint8_t x; for (x = 0; x != USB_MAX_AUTO_QUIRK; x++) { if (udev->autoQuirk[x] == 0 || udev->autoQuirk[x] == quirk) { udev->autoQuirk[x] = quirk; return (0); /* success */ } } return (USB_ERR_NOMEM); } /* * The following function is used to select the endpoint mode. It * should not be called outside enumeration context. */ usb_error_t usbd_set_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep, uint8_t ep_mode) { usb_error_t error; uint8_t do_unlock; /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); if (udev->bus->methods->set_endpoint_mode != NULL) { error = (udev->bus->methods->set_endpoint_mode) ( udev, ep, ep_mode); } else if (ep_mode != USB_EP_MODE_DEFAULT) { error = USB_ERR_INVAL; } else { error = 0; } /* only set new mode regardless of error */ ep->ep_mode = ep_mode; if (do_unlock) usbd_enum_unlock(udev); return (error); } uint8_t usbd_get_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep) { return (ep->ep_mode); } Index: projects/clang390-import/sys/dev/usb/usb_device.h =================================================================== --- projects/clang390-import/sys/dev/usb/usb_device.h (revision 305430) +++ projects/clang390-import/sys/dev/usb/usb_device.h (revision 305431) @@ -1,331 +1,334 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _USB_DEVICE_H_ #define _USB_DEVICE_H_ #ifndef USB_GLOBAL_INCLUDE_FILE #include #include #include #endif struct usb_bus_methods; struct usb_config_descriptor; struct usb_device; /* linux compat */ struct usb_fs_privdata; struct usb_hw_ep_profile; struct usb_symlink; /* UGEN */ #define USB_CTRL_XFER_MAX 2 /* "usb_config_parse()" commands */ #define USB_CFG_ALLOC 0 #define USB_CFG_FREE 1 #define USB_CFG_INIT 2 /* "usb_unconfigure()" flags */ #define USB_UNCFG_FLAG_NONE 0x00 #define USB_UNCFG_FLAG_FREE_EP0 0x02 /* endpoint zero is freed */ struct usb_udev_msg { struct usb_proc_msg hdr; struct usb_device *udev; }; /* The following four structures makes up a tree, where we have the * leaf structure, "usb_host_endpoint", first, and the root structure, * "usb_device", last. The four structures below mirror the structure * of the USB descriptors belonging to an USB configuration. Please * refer to the USB specification for a definition of "endpoints" and * "interfaces". */ struct usb_host_endpoint { struct usb_endpoint_descriptor desc; TAILQ_HEAD(, urb) bsd_urb_list; struct usb_xfer *bsd_xfer[2]; uint8_t *extra; /* Extra descriptors */ usb_frlength_t fbsd_buf_size; uint16_t extralen; uint8_t bsd_iface_index; } __aligned(USB_HOST_ALIGN); struct usb_host_interface { struct usb_interface_descriptor desc; /* the following array has size "desc.bNumEndpoint" */ struct usb_host_endpoint *endpoint; const char *string; /* iInterface string, if present */ uint8_t *extra; /* Extra descriptors */ uint16_t extralen; uint8_t bsd_iface_index; } __aligned(USB_HOST_ALIGN); /* * The following structure defines the USB device flags. */ struct usb_device_flags { enum usb_hc_mode usb_mode; /* host or device mode */ uint8_t self_powered:1; /* set if USB device is self powered */ uint8_t no_strings:1; /* set if USB device does not support * strings */ uint8_t remote_wakeup:1; /* set if remote wakeup is enabled */ uint8_t uq_bus_powered:1; /* set if BUS powered quirk is present */ /* * NOTE: Although the flags below will reach the same value * over time, but the instant values may differ, and * consequently the flags cannot be merged into one! */ uint8_t peer_suspended:1; /* set if peer is suspended */ uint8_t self_suspended:1; /* set if self is suspended */ }; /* * The following structure is used for power-save purposes. The data * in this structure is protected by the USB BUS lock. */ struct usb_power_save { usb_ticks_t last_xfer_time; /* copy of "ticks" */ usb_size_t type_refs[4]; /* transfer reference count */ usb_size_t read_refs; /* data read references */ usb_size_t write_refs; /* data write references */ }; /* * The following structure is used when trying to allocate hardware * endpoints for an USB configuration in USB device side mode. */ struct usb_hw_ep_scratch_sub { const struct usb_hw_ep_profile *pf; uint16_t max_frame_size; uint8_t hw_endpoint_out; uint8_t hw_endpoint_in; uint8_t needs_ep_type; uint8_t needs_in:1; uint8_t needs_out:1; }; /* * The following structure is used when trying to allocate hardware * endpoints for an USB configuration in USB device side mode. */ struct usb_hw_ep_scratch { struct usb_hw_ep_scratch_sub ep[USB_EP_MAX]; struct usb_hw_ep_scratch_sub *ep_max; struct usb_config_descriptor *cd; struct usb_device *udev; const struct usb_bus_methods *methods; uint8_t bmOutAlloc[(USB_EP_MAX + 15) / 16]; uint8_t bmInAlloc[(USB_EP_MAX + 15) / 16]; }; /* * The following structure is used when generating USB descriptors * from USB templates. */ struct usb_temp_setup { void *buf; usb_size_t size; enum usb_dev_speed usb_speed; uint8_t self_powered; uint8_t bNumEndpoints; uint8_t bInterfaceNumber; uint8_t bAlternateSetting; uint8_t bConfigurationValue; usb_error_t err; }; /* * The scratch area for USB devices. Access to this structure is - * protected by the enumeration SX lock. + * protected by the control SX lock. */ union usb_device_scratch { struct usb_hw_ep_scratch hw_ep_scratch[1]; struct usb_temp_setup temp_setup[1]; struct { struct usb_xfer dummy; struct usb_setup_params parm; } xfer_setup[1]; uint8_t data[255]; }; /* * The following structure defines an USB device. There exists one of * these structures for every USB device. */ struct usb_device { /* generic clear stall message */ struct usb_udev_msg cs_msg[2]; struct sx enum_sx; struct sx sr_sx; + struct sx ctrl_sx; struct mtx device_mtx; struct cv ctrlreq_cv; struct cv ref_cv; #if (USB_HAVE_FIXED_IFACE == 0) struct usb_interface *ifaces; #else struct usb_interface ifaces[USB_IFACE_MAX]; #endif struct usb_endpoint ctrl_ep; /* Control Endpoint 0 */ #if (USB_HAVE_FIXED_ENDPOINT == 0) struct usb_endpoint *endpoints; #else struct usb_endpoint endpoints[USB_MAX_EP_UNITS]; #endif struct usb_power_save pwr_save;/* power save data */ struct usb_bus *bus; /* our USB BUS */ device_t parent_dev; /* parent device */ struct usb_device *parent_hub; struct usb_device *parent_hs_hub; /* high-speed parent HUB */ struct usb_config_descriptor *cdesc; /* full config descr */ struct usb_hub *hub; /* only if this is a hub */ struct usb_xfer *ctrl_xfer[USB_CTRL_XFER_MAX]; struct usb_temp_data *usb_template_ptr; struct usb_endpoint *ep_curr; /* current clear stall endpoint */ #if USB_HAVE_UGEN struct usb_fifo *fifo[USB_FIFO_MAX]; struct usb_symlink *ugen_symlink; /* our generic symlink */ struct usb_fs_privdata *ctrl_dev; /* Control Endpoint 0 device node */ LIST_HEAD(,usb_fs_privdata) pd_list; char ugen_name[20]; /* name of ugenX.X device */ #endif usb_ticks_t plugtime; /* copy of "ticks" */ enum usb_dev_state state; enum usb_dev_speed speed; uint16_t refcount; #define USB_DEV_REF_MAX 0xffff uint16_t power; /* mA the device uses */ uint16_t langid; /* language for strings */ uint16_t autoQuirk[USB_MAX_AUTO_QUIRK]; /* dynamic quirks */ uint8_t address; /* device addess */ uint8_t device_index; /* device index in "bus->devices" */ uint8_t controller_slot_id; /* controller specific value */ uint8_t next_config_index; /* used by USB_RE_ENUM_SET_CONFIG */ uint8_t curr_config_index; /* current configuration index */ uint8_t curr_config_no; /* current configuration number */ uint8_t depth; /* distance from root HUB */ uint8_t port_index; /* parent HUB port index */ uint8_t port_no; /* parent HUB port number */ uint8_t hs_hub_addr; /* high-speed HUB address */ uint8_t hs_port_no; /* high-speed HUB port number */ uint8_t driver_added_refcount; /* our driver added generation count */ uint8_t power_mode; /* see USB_POWER_XXX */ uint8_t re_enumerate_wait; /* set if re-enum. is in progress */ #define USB_RE_ENUM_DONE 0 #define USB_RE_ENUM_START 1 #define USB_RE_ENUM_PWR_OFF 2 #define USB_RE_ENUM_SET_CONFIG 3 uint8_t ifaces_max; /* number of interfaces present */ uint8_t endpoints_max; /* number of endpoints present */ /* the "flags" field is write-protected by "bus->mtx" */ struct usb_device_flags flags; struct usb_endpoint_descriptor ctrl_ep_desc; /* for endpoint 0 */ struct usb_endpoint_ss_comp_descriptor ctrl_ep_comp_desc; /* for endpoint 0 */ struct usb_device_descriptor ddesc; /* device descriptor */ char *serial; /* serial number, can be NULL */ char *manufacturer; /* manufacturer string, can be NULL */ char *product; /* product string, can be NULL */ #if USB_HAVE_COMPAT_LINUX /* Linux compat */ struct usb_device_descriptor descriptor; struct usb_host_endpoint ep0; struct usb_interface *linux_iface_start; struct usb_interface *linux_iface_end; struct usb_host_endpoint *linux_endpoint_start; struct usb_host_endpoint *linux_endpoint_end; uint16_t devnum; #endif uint32_t clear_stall_errors; /* number of clear-stall failures */ union usb_device_scratch scratch; #if (USB_HAVE_FIXED_CONFIG != 0) uint32_t config_data[(USB_CONFIG_MAX + 3) / 4]; #endif }; /* globals */ extern int usb_template; /* function prototypes */ const char *usb_statestr(enum usb_dev_state state); struct usb_device *usb_alloc_device(device_t parent_dev, struct usb_bus *bus, struct usb_device *parent_hub, uint8_t depth, uint8_t port_index, uint8_t port_no, enum usb_dev_speed speed, enum usb_hc_mode mode); #if USB_HAVE_UGEN struct usb_fs_privdata *usb_make_dev(struct usb_device *, const char *, int, int, int, uid_t, gid_t, int); void usb_destroy_dev(struct usb_fs_privdata *); void usb_destroy_dev_sync(struct usb_fs_privdata *); #endif usb_error_t usb_probe_and_attach(struct usb_device *udev, uint8_t iface_index); void usb_detach_device(struct usb_device *, uint8_t, uint8_t); usb_error_t usb_reset_iface_endpoints(struct usb_device *udev, uint8_t iface_index); usb_error_t usbd_set_config_index(struct usb_device *udev, uint8_t index); usb_error_t usbd_set_endpoint_stall(struct usb_device *udev, struct usb_endpoint *ep, uint8_t do_stall); usb_error_t usb_suspend_resume(struct usb_device *udev, uint8_t do_suspend); void usb_devinfo(struct usb_device *udev, char *dst_ptr, uint16_t dst_len); void usb_free_device(struct usb_device *, uint8_t); void usb_linux_free_device(struct usb_device *dev); uint8_t usb_peer_can_wakeup(struct usb_device *udev); struct usb_endpoint *usb_endpoint_foreach(struct usb_device *udev, struct usb_endpoint *ep); void usb_set_device_state(struct usb_device *, enum usb_dev_state); enum usb_dev_state usb_get_device_state(struct usb_device *); uint8_t usbd_enum_lock(struct usb_device *); #if USB_HAVE_UGEN uint8_t usbd_enum_lock_sig(struct usb_device *); #endif void usbd_enum_unlock(struct usb_device *); void usbd_sr_lock(struct usb_device *); void usbd_sr_unlock(struct usb_device *); +uint8_t usbd_ctrl_lock(struct usb_device *); +void usbd_ctrl_unlock(struct usb_device *); uint8_t usbd_enum_is_locked(struct usb_device *); #if USB_HAVE_TT_SUPPORT void uhub_tt_buffer_reset_async_locked(struct usb_device *, struct usb_endpoint *); #endif uint8_t uhub_count_active_host_ports(struct usb_device *, enum usb_dev_speed); #endif /* _USB_DEVICE_H_ */ Index: projects/clang390-import/sys/dev/usb/usb_generic.c =================================================================== --- projects/clang390-import/sys/dev/usb/usb_generic.c (revision 305430) +++ projects/clang390-import/sys/dev/usb/usb_generic.c (revision 305431) @@ -1,2335 +1,2338 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR ugen_debug #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ #if USB_HAVE_UGEN /* defines */ #define UGEN_BULK_FS_BUFFER_SIZE (64*32) /* bytes */ #define UGEN_BULK_HS_BUFFER_SIZE (1024*32) /* bytes */ #define UGEN_HW_FRAMES 50 /* number of milliseconds per transfer */ /* function prototypes */ static usb_callback_t ugen_read_clear_stall_callback; static usb_callback_t ugen_write_clear_stall_callback; static usb_callback_t ugen_ctrl_read_callback; static usb_callback_t ugen_ctrl_write_callback; static usb_callback_t ugen_isoc_read_callback; static usb_callback_t ugen_isoc_write_callback; static usb_callback_t ugen_ctrl_fs_callback; static usb_fifo_open_t ugen_open; static usb_fifo_close_t ugen_close; static usb_fifo_ioctl_t ugen_ioctl; static usb_fifo_ioctl_t ugen_ioctl_post; static usb_fifo_cmd_t ugen_start_read; static usb_fifo_cmd_t ugen_start_write; static usb_fifo_cmd_t ugen_stop_io; static int ugen_transfer_setup(struct usb_fifo *, const struct usb_config *, uint8_t); static int ugen_open_pipe_write(struct usb_fifo *); static int ugen_open_pipe_read(struct usb_fifo *); static int ugen_set_config(struct usb_fifo *, uint8_t); static int ugen_set_interface(struct usb_fifo *, uint8_t, uint8_t); static int ugen_get_cdesc(struct usb_fifo *, struct usb_gen_descriptor *); static int ugen_get_sdesc(struct usb_fifo *, struct usb_gen_descriptor *); static int ugen_get_iface_driver(struct usb_fifo *f, struct usb_gen_descriptor *ugd); static int usb_gen_fill_deviceinfo(struct usb_fifo *, struct usb_device_info *); static int ugen_re_enumerate(struct usb_fifo *); static int ugen_iface_ioctl(struct usb_fifo *, u_long, void *, int); static uint8_t ugen_fs_get_complete(struct usb_fifo *, uint8_t *); static int ugen_fs_uninit(struct usb_fifo *f); /* structures */ struct usb_fifo_methods usb_ugen_methods = { .f_open = &ugen_open, .f_close = &ugen_close, .f_ioctl = &ugen_ioctl, .f_ioctl_post = &ugen_ioctl_post, .f_start_read = &ugen_start_read, .f_stop_read = &ugen_stop_io, .f_start_write = &ugen_start_write, .f_stop_write = &ugen_stop_io, }; #ifdef USB_DEBUG static int ugen_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, ugen, CTLFLAG_RW, 0, "USB generic"); SYSCTL_INT(_hw_usb_ugen, OID_AUTO, debug, CTLFLAG_RWTUN, &ugen_debug, 0, "Debug level"); #endif /* prototypes */ static int ugen_transfer_setup(struct usb_fifo *f, const struct usb_config *setup, uint8_t n_setup) { struct usb_endpoint *ep = usb_fifo_softc(f); struct usb_device *udev = f->udev; uint8_t iface_index = ep->iface_index; int error; mtx_unlock(f->priv_mtx); /* * "usbd_transfer_setup()" can sleep so one needs to make a wrapper, * exiting the mutex and checking things */ error = usbd_transfer_setup(udev, &iface_index, f->xfer, setup, n_setup, f, f->priv_mtx); if (error == 0) { if (f->xfer[0]->nframes == 1) { error = usb_fifo_alloc_buffer(f, f->xfer[0]->max_data_length, 2); } else { error = usb_fifo_alloc_buffer(f, f->xfer[0]->max_frame_size, 2 * f->xfer[0]->nframes); } if (error) { usbd_transfer_unsetup(f->xfer, n_setup); } } mtx_lock(f->priv_mtx); return (error); } static int ugen_open(struct usb_fifo *f, int fflags) { struct usb_endpoint *ep = usb_fifo_softc(f); struct usb_endpoint_descriptor *ed = ep->edesc; uint8_t type; DPRINTFN(6, "flag=0x%x\n", fflags); mtx_lock(f->priv_mtx); switch (usbd_get_speed(f->udev)) { case USB_SPEED_LOW: case USB_SPEED_FULL: f->nframes = UGEN_HW_FRAMES; f->bufsize = UGEN_BULK_FS_BUFFER_SIZE; break; default: f->nframes = UGEN_HW_FRAMES * 8; f->bufsize = UGEN_BULK_HS_BUFFER_SIZE; break; } type = ed->bmAttributes & UE_XFERTYPE; if (type == UE_INTERRUPT) { f->bufsize = 0; /* use "wMaxPacketSize" */ } f->timeout = USB_NO_TIMEOUT; f->flag_short = 0; f->fifo_zlp = 0; mtx_unlock(f->priv_mtx); return (0); } static void ugen_close(struct usb_fifo *f, int fflags) { DPRINTFN(6, "flag=0x%x\n", fflags); /* cleanup */ mtx_lock(f->priv_mtx); usbd_transfer_stop(f->xfer[0]); usbd_transfer_stop(f->xfer[1]); mtx_unlock(f->priv_mtx); usbd_transfer_unsetup(f->xfer, 2); usb_fifo_free_buffer(f); if (ugen_fs_uninit(f)) { /* ignore any errors - we are closing */ DPRINTFN(6, "no FIFOs\n"); } } static int ugen_open_pipe_write(struct usb_fifo *f) { struct usb_config usb_config[2]; struct usb_endpoint *ep = usb_fifo_softc(f); struct usb_endpoint_descriptor *ed = ep->edesc; mtx_assert(f->priv_mtx, MA_OWNED); if (f->xfer[0] || f->xfer[1]) { /* transfers are already opened */ return (0); } memset(usb_config, 0, sizeof(usb_config)); usb_config[1].type = UE_CONTROL; usb_config[1].endpoint = 0; usb_config[1].direction = UE_DIR_ANY; usb_config[1].timeout = 1000; /* 1 second */ usb_config[1].interval = 50;/* 50 milliseconds */ usb_config[1].bufsize = sizeof(struct usb_device_request); usb_config[1].callback = &ugen_write_clear_stall_callback; usb_config[1].usb_mode = USB_MODE_HOST; usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; usb_config[0].stream_id = 0; /* XXX support more stream ID's */ usb_config[0].direction = UE_DIR_TX; usb_config[0].interval = USB_DEFAULT_INTERVAL; usb_config[0].flags.proxy_buffer = 1; usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ switch (ed->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: case UE_BULK: if (f->flag_short) { usb_config[0].flags.force_short_xfer = 1; } usb_config[0].callback = &ugen_ctrl_write_callback; usb_config[0].timeout = f->timeout; usb_config[0].frames = 1; usb_config[0].bufsize = f->bufsize; if (ugen_transfer_setup(f, usb_config, 2)) { return (EIO); } /* first transfer does not clear stall */ f->flag_stall = 0; break; case UE_ISOCHRONOUS: usb_config[0].flags.short_xfer_ok = 1; usb_config[0].bufsize = 0; /* use default */ usb_config[0].frames = f->nframes; usb_config[0].callback = &ugen_isoc_write_callback; usb_config[0].timeout = 0; /* clone configuration */ usb_config[1] = usb_config[0]; if (ugen_transfer_setup(f, usb_config, 2)) { return (EIO); } break; default: return (EINVAL); } return (0); } static int ugen_open_pipe_read(struct usb_fifo *f) { struct usb_config usb_config[2]; struct usb_endpoint *ep = usb_fifo_softc(f); struct usb_endpoint_descriptor *ed = ep->edesc; mtx_assert(f->priv_mtx, MA_OWNED); if (f->xfer[0] || f->xfer[1]) { /* transfers are already opened */ return (0); } memset(usb_config, 0, sizeof(usb_config)); usb_config[1].type = UE_CONTROL; usb_config[1].endpoint = 0; usb_config[1].direction = UE_DIR_ANY; usb_config[1].timeout = 1000; /* 1 second */ usb_config[1].interval = 50;/* 50 milliseconds */ usb_config[1].bufsize = sizeof(struct usb_device_request); usb_config[1].callback = &ugen_read_clear_stall_callback; usb_config[1].usb_mode = USB_MODE_HOST; usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; usb_config[0].stream_id = 0; /* XXX support more stream ID's */ usb_config[0].direction = UE_DIR_RX; usb_config[0].interval = USB_DEFAULT_INTERVAL; usb_config[0].flags.proxy_buffer = 1; usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ switch (ed->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: case UE_BULK: if (f->flag_short) { usb_config[0].flags.short_xfer_ok = 1; } usb_config[0].timeout = f->timeout; usb_config[0].frames = 1; usb_config[0].callback = &ugen_ctrl_read_callback; usb_config[0].bufsize = f->bufsize; if (ugen_transfer_setup(f, usb_config, 2)) { return (EIO); } /* first transfer does not clear stall */ f->flag_stall = 0; break; case UE_ISOCHRONOUS: usb_config[0].flags.short_xfer_ok = 1; usb_config[0].bufsize = 0; /* use default */ usb_config[0].frames = f->nframes; usb_config[0].callback = &ugen_isoc_read_callback; usb_config[0].timeout = 0; /* clone configuration */ usb_config[1] = usb_config[0]; if (ugen_transfer_setup(f, usb_config, 2)) { return (EIO); } break; default: return (EINVAL); } return (0); } static void ugen_start_read(struct usb_fifo *f) { /* check that pipes are open */ if (ugen_open_pipe_read(f)) { /* signal error */ usb_fifo_put_data_error(f); } /* start transfers */ usbd_transfer_start(f->xfer[0]); usbd_transfer_start(f->xfer[1]); } static void ugen_start_write(struct usb_fifo *f) { /* check that pipes are open */ if (ugen_open_pipe_write(f)) { /* signal error */ usb_fifo_get_data_error(f); } /* start transfers */ usbd_transfer_start(f->xfer[0]); usbd_transfer_start(f->xfer[1]); } static void ugen_stop_io(struct usb_fifo *f) { /* stop transfers */ usbd_transfer_stop(f->xfer[0]); usbd_transfer_stop(f->xfer[1]); } static void ugen_ctrl_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); struct usb_mbuf *m; DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (xfer->actlen == 0) { if (f->fifo_zlp != 4) { f->fifo_zlp++; } else { /* * Throttle a little bit we have multiple ZLPs * in a row! */ xfer->interval = 64; /* ms */ } } else { /* clear throttle */ xfer->interval = 0; f->fifo_zlp = 0; } usb_fifo_put_data(f, xfer->frbuffers, 0, xfer->actlen, 1); case USB_ST_SETUP: if (f->flag_stall) { usbd_transfer_start(f->xfer[1]); break; } USB_IF_POLL(&f->free_q, m); if (m) { usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); } break; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* send a zero length packet to userland */ usb_fifo_put_data(f, xfer->frbuffers, 0, 0, 1); f->flag_stall = 1; f->fifo_zlp = 0; usbd_transfer_start(f->xfer[1]); } break; } } static void ugen_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); usb_frlength_t actlen; DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: /* * If writing is in stall, just jump to clear stall * callback and solve the situation. */ if (f->flag_stall) { usbd_transfer_start(f->xfer[1]); break; } /* * Write data, setup and perform hardware transfer. */ if (usb_fifo_get_data(f, xfer->frbuffers, 0, xfer->max_data_length, &actlen, 0)) { usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } break; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { f->flag_stall = 1; usbd_transfer_start(f->xfer[1]); } break; } } static void ugen_read_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); struct usb_xfer *xfer_other = f->xfer[0]; if (f->flag_stall == 0) { /* nothing to do */ return; } if (usbd_clear_stall_callback(xfer, xfer_other)) { DPRINTFN(5, "f=%p: stall cleared\n", f); f->flag_stall = 0; usbd_transfer_start(xfer_other); } } static void ugen_write_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); struct usb_xfer *xfer_other = f->xfer[0]; if (f->flag_stall == 0) { /* nothing to do */ return; } if (usbd_clear_stall_callback(xfer, xfer_other)) { DPRINTFN(5, "f=%p: stall cleared\n", f); f->flag_stall = 0; usbd_transfer_start(xfer_other); } } static void ugen_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); usb_frlength_t offset; usb_frcount_t n; DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(6, "actlen=%d\n", xfer->actlen); offset = 0; for (n = 0; n != xfer->aframes; n++) { usb_fifo_put_data(f, xfer->frbuffers, offset, xfer->frlengths[n], 1); offset += xfer->max_frame_size; } case USB_ST_SETUP: tr_setup: for (n = 0; n != xfer->nframes; n++) { /* setup size for next transfer */ usbd_xfer_set_frame_len(xfer, n, xfer->max_frame_size); } usbd_transfer_submit(xfer); break; default: /* Error */ if (xfer->error == USB_ERR_CANCELLED) { break; } goto tr_setup; } } static void ugen_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); usb_frlength_t actlen; usb_frlength_t offset; usb_frcount_t n; DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: case USB_ST_SETUP: tr_setup: offset = 0; for (n = 0; n != xfer->nframes; n++) { if (usb_fifo_get_data(f, xfer->frbuffers, offset, xfer->max_frame_size, &actlen, 1)) { usbd_xfer_set_frame_len(xfer, n, actlen); offset += actlen; } else { break; } } for (; n != xfer->nframes; n++) { /* fill in zero frames */ usbd_xfer_set_frame_len(xfer, n, 0); } usbd_transfer_submit(xfer); break; default: /* Error */ if (xfer->error == USB_ERR_CANCELLED) { break; } goto tr_setup; } } static int ugen_set_config(struct usb_fifo *f, uint8_t index) { DPRINTFN(2, "index %u\n", index); if (f->udev->flags.usb_mode != USB_MODE_HOST) { /* not possible in device side mode */ return (ENOTTY); } /* make sure all FIFO's are gone */ /* else there can be a deadlock */ if (ugen_fs_uninit(f)) { /* ignore any errors */ DPRINTFN(6, "no FIFOs\n"); } if (usbd_start_set_config(f->udev, index) != 0) return (EIO); return (0); } static int ugen_set_interface(struct usb_fifo *f, uint8_t iface_index, uint8_t alt_index) { DPRINTFN(2, "%u, %u\n", iface_index, alt_index); if (f->udev->flags.usb_mode != USB_MODE_HOST) { /* not possible in device side mode */ return (ENOTTY); } /* make sure all FIFO's are gone */ /* else there can be a deadlock */ if (ugen_fs_uninit(f)) { /* ignore any errors */ DPRINTFN(6, "no FIFOs\n"); } /* change setting - will free generic FIFOs, if any */ if (usbd_set_alt_interface_index(f->udev, iface_index, alt_index)) { return (EIO); } /* probe and attach */ if (usb_probe_and_attach(f->udev, iface_index)) { return (EIO); } return (0); } /*------------------------------------------------------------------------* * ugen_get_cdesc * * This function will retrieve the complete configuration descriptor * at the given index. *------------------------------------------------------------------------*/ static int ugen_get_cdesc(struct usb_fifo *f, struct usb_gen_descriptor *ugd) { struct usb_config_descriptor *cdesc; struct usb_device *udev = f->udev; int error; uint16_t len; uint8_t free_data; DPRINTFN(6, "\n"); if (ugd->ugd_data == NULL) { /* userland pointer should not be zero */ return (EINVAL); } if ((ugd->ugd_config_index == USB_UNCONFIG_INDEX) || (ugd->ugd_config_index == udev->curr_config_index)) { cdesc = usbd_get_config_descriptor(udev); if (cdesc == NULL) return (ENXIO); free_data = 0; } else { #if (USB_HAVE_FIXED_CONFIG == 0) if (usbd_req_get_config_desc_full(udev, NULL, &cdesc, ugd->ugd_config_index)) { return (ENXIO); } free_data = 1; #else /* configuration descriptor data is shared */ return (EINVAL); #endif } len = UGETW(cdesc->wTotalLength); if (len > ugd->ugd_maxlen) { len = ugd->ugd_maxlen; } DPRINTFN(6, "len=%u\n", len); ugd->ugd_actlen = len; ugd->ugd_offset = 0; error = copyout(cdesc, ugd->ugd_data, len); if (free_data) usbd_free_config_desc(udev, cdesc); return (error); } -/* - * This function is called having the enumeration SX locked which - * protects the scratch area used. - */ static int ugen_get_sdesc(struct usb_fifo *f, struct usb_gen_descriptor *ugd) { void *ptr; uint16_t size; int error; + uint8_t do_unlock; + /* Protect scratch area */ + do_unlock = usbd_ctrl_lock(f->udev); + ptr = f->udev->scratch.data; size = sizeof(f->udev->scratch.data); if (usbd_req_get_string_desc(f->udev, NULL, ptr, size, ugd->ugd_lang_id, ugd->ugd_string_index)) { error = EINVAL; } else { if (size > ((uint8_t *)ptr)[0]) { size = ((uint8_t *)ptr)[0]; } if (size > ugd->ugd_maxlen) { size = ugd->ugd_maxlen; } ugd->ugd_actlen = size; ugd->ugd_offset = 0; error = copyout(ptr, ugd->ugd_data, size); } + if (do_unlock) + usbd_ctrl_unlock(f->udev); + return (error); } /*------------------------------------------------------------------------* * ugen_get_iface_driver * * This function generates an USB interface description for userland. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static int ugen_get_iface_driver(struct usb_fifo *f, struct usb_gen_descriptor *ugd) { struct usb_device *udev = f->udev; struct usb_interface *iface; const char *ptr; const char *desc; unsigned int len; unsigned int maxlen; char buf[128]; int error; DPRINTFN(6, "\n"); if ((ugd->ugd_data == NULL) || (ugd->ugd_maxlen == 0)) { /* userland pointer should not be zero */ return (EINVAL); } iface = usbd_get_iface(udev, ugd->ugd_iface_index); if ((iface == NULL) || (iface->idesc == NULL)) { /* invalid interface index */ return (EINVAL); } /* read out device nameunit string, if any */ if ((iface->subdev != NULL) && device_is_attached(iface->subdev) && (ptr = device_get_nameunit(iface->subdev)) && (desc = device_get_desc(iface->subdev))) { /* print description */ snprintf(buf, sizeof(buf), "%s: <%s>", ptr, desc); /* range checks */ maxlen = ugd->ugd_maxlen - 1; len = strlen(buf); if (len > maxlen) len = maxlen; /* update actual length, including terminating zero */ ugd->ugd_actlen = len + 1; /* copy out interface description */ error = copyout(buf, ugd->ugd_data, ugd->ugd_actlen); } else { /* zero length string is default */ error = copyout("", ugd->ugd_data, 1); } return (error); } /*------------------------------------------------------------------------* * usb_gen_fill_deviceinfo * * This function dumps information about an USB device to the * structure pointed to by the "di" argument. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static int usb_gen_fill_deviceinfo(struct usb_fifo *f, struct usb_device_info *di) { struct usb_device *udev; struct usb_device *hub; udev = f->udev; bzero(di, sizeof(di[0])); di->udi_bus = device_get_unit(udev->bus->bdev); di->udi_addr = udev->address; di->udi_index = udev->device_index; strlcpy(di->udi_serial, usb_get_serial(udev), sizeof(di->udi_serial)); strlcpy(di->udi_vendor, usb_get_manufacturer(udev), sizeof(di->udi_vendor)); strlcpy(di->udi_product, usb_get_product(udev), sizeof(di->udi_product)); usb_printbcd(di->udi_release, sizeof(di->udi_release), UGETW(udev->ddesc.bcdDevice)); di->udi_vendorNo = UGETW(udev->ddesc.idVendor); di->udi_productNo = UGETW(udev->ddesc.idProduct); di->udi_releaseNo = UGETW(udev->ddesc.bcdDevice); di->udi_class = udev->ddesc.bDeviceClass; di->udi_subclass = udev->ddesc.bDeviceSubClass; di->udi_protocol = udev->ddesc.bDeviceProtocol; di->udi_config_no = udev->curr_config_no; di->udi_config_index = udev->curr_config_index; di->udi_power = udev->flags.self_powered ? 0 : udev->power; di->udi_speed = udev->speed; di->udi_mode = udev->flags.usb_mode; di->udi_power_mode = udev->power_mode; di->udi_suspended = udev->flags.peer_suspended; hub = udev->parent_hub; if (hub) { di->udi_hubaddr = hub->address; di->udi_hubindex = hub->device_index; di->udi_hubport = udev->port_no; } return (0); } /*------------------------------------------------------------------------* * ugen_check_request * * Return values: * 0: Access allowed * Else: No access *------------------------------------------------------------------------*/ static int ugen_check_request(struct usb_device *udev, struct usb_device_request *req) { struct usb_endpoint *ep; int error; /* * Avoid requests that would damage the bus integrity: */ if (((req->bmRequestType == UT_WRITE_DEVICE) && (req->bRequest == UR_SET_ADDRESS)) || ((req->bmRequestType == UT_WRITE_DEVICE) && (req->bRequest == UR_SET_CONFIG)) || ((req->bmRequestType == UT_WRITE_INTERFACE) && (req->bRequest == UR_SET_INTERFACE))) { /* * These requests can be useful for testing USB drivers. */ error = priv_check(curthread, PRIV_DRIVER); if (error) { return (error); } } /* * Special case - handle clearing of stall */ if (req->bmRequestType == UT_WRITE_ENDPOINT) { ep = usbd_get_ep_by_addr(udev, req->wIndex[0]); if (ep == NULL) { return (EINVAL); } if ((req->bRequest == UR_CLEAR_FEATURE) && (UGETW(req->wValue) == UF_ENDPOINT_HALT)) { usbd_clear_data_toggle(udev, ep); } } /* TODO: add more checks to verify the interface index */ return (0); } int ugen_do_request(struct usb_fifo *f, struct usb_ctl_request *ur) { int error; uint16_t len; uint16_t actlen; if (ugen_check_request(f->udev, &ur->ucr_request)) { return (EPERM); } len = UGETW(ur->ucr_request.wLength); /* check if "ucr_data" is valid */ if (len != 0) { if (ur->ucr_data == NULL) { return (EFAULT); } } /* do the USB request */ error = usbd_do_request_flags (f->udev, NULL, &ur->ucr_request, ur->ucr_data, (ur->ucr_flags & USB_SHORT_XFER_OK) | USB_USER_DATA_PTR, &actlen, USB_DEFAULT_TIMEOUT); ur->ucr_actlen = actlen; if (error) { error = EIO; } return (error); } /*------------------------------------------------------------------------ * ugen_re_enumerate *------------------------------------------------------------------------*/ static int ugen_re_enumerate(struct usb_fifo *f) { struct usb_device *udev = f->udev; int error; /* * This request can be useful for testing USB drivers: */ error = priv_check(curthread, PRIV_DRIVER); if (error) { return (error); } if (udev->flags.usb_mode != USB_MODE_HOST) { /* not possible in device side mode */ DPRINTFN(6, "device mode\n"); return (ENOTTY); } /* make sure all FIFO's are gone */ /* else there can be a deadlock */ if (ugen_fs_uninit(f)) { /* ignore any errors */ DPRINTFN(6, "no FIFOs\n"); } /* start re-enumeration of device */ usbd_start_re_enumerate(udev); return (0); } int ugen_fs_uninit(struct usb_fifo *f) { if (f->fs_xfer == NULL) { return (EINVAL); } usbd_transfer_unsetup(f->fs_xfer, f->fs_ep_max); free(f->fs_xfer, M_USB); f->fs_xfer = NULL; f->fs_ep_max = 0; f->fs_ep_ptr = NULL; f->flag_iscomplete = 0; usb_fifo_free_buffer(f); return (0); } static uint8_t ugen_fs_get_complete(struct usb_fifo *f, uint8_t *pindex) { struct usb_mbuf *m; USB_IF_DEQUEUE(&f->used_q, m); if (m) { *pindex = *((uint8_t *)(m->cur_data_ptr)); USB_IF_ENQUEUE(&f->free_q, m); return (0); /* success */ } else { *pindex = 0; /* fix compiler warning */ f->flag_iscomplete = 0; } return (1); /* failure */ } static void ugen_fs_set_complete(struct usb_fifo *f, uint8_t index) { struct usb_mbuf *m; USB_IF_DEQUEUE(&f->free_q, m); if (m == NULL) { /* can happen during close */ DPRINTF("out of buffers\n"); return; } USB_MBUF_RESET(m); *((uint8_t *)(m->cur_data_ptr)) = index; USB_IF_ENQUEUE(&f->used_q, m); f->flag_iscomplete = 1; usb_fifo_wakeup(f); } static int ugen_fs_copy_in(struct usb_fifo *f, uint8_t ep_index) { struct usb_device_request *req; struct usb_xfer *xfer; struct usb_fs_endpoint fs_ep; void *uaddr; /* userland pointer */ void *kaddr; usb_frlength_t offset; usb_frlength_t rem; usb_frcount_t n; uint32_t length; int error; uint8_t isread; if (ep_index >= f->fs_ep_max) { return (EINVAL); } xfer = f->fs_xfer[ep_index]; if (xfer == NULL) { return (EINVAL); } mtx_lock(f->priv_mtx); if (usbd_transfer_pending(xfer)) { mtx_unlock(f->priv_mtx); return (EBUSY); /* should not happen */ } mtx_unlock(f->priv_mtx); error = copyin(f->fs_ep_ptr + ep_index, &fs_ep, sizeof(fs_ep)); if (error) { return (error); } /* security checks */ if (fs_ep.nFrames > xfer->max_frame_count) { xfer->error = USB_ERR_INVAL; goto complete; } if (fs_ep.nFrames == 0) { xfer->error = USB_ERR_INVAL; goto complete; } error = copyin(fs_ep.ppBuffer, &uaddr, sizeof(uaddr)); if (error) { return (error); } /* reset first frame */ usbd_xfer_set_frame_offset(xfer, 0, 0); if (xfer->flags_int.control_xfr) { req = xfer->frbuffers[0].buffer; error = copyin(fs_ep.pLength, &length, sizeof(length)); if (error) { return (error); } if (length != sizeof(*req)) { xfer->error = USB_ERR_INVAL; goto complete; } if (length != 0) { error = copyin(uaddr, req, length); if (error) { return (error); } } if (ugen_check_request(f->udev, req)) { xfer->error = USB_ERR_INVAL; goto complete; } usbd_xfer_set_frame_len(xfer, 0, length); /* Host mode only ! */ if ((req->bmRequestType & (UT_READ | UT_WRITE)) == UT_READ) { isread = 1; } else { isread = 0; } n = 1; offset = sizeof(*req); } else { /* Device and Host mode */ if (USB_GET_DATA_ISREAD(xfer)) { isread = 1; } else { isread = 0; } n = 0; offset = 0; } rem = usbd_xfer_max_len(xfer); xfer->nframes = fs_ep.nFrames; xfer->timeout = fs_ep.timeout; if (xfer->timeout > 65535) { xfer->timeout = 65535; } if (fs_ep.flags & USB_FS_FLAG_SINGLE_SHORT_OK) xfer->flags.short_xfer_ok = 1; else xfer->flags.short_xfer_ok = 0; if (fs_ep.flags & USB_FS_FLAG_MULTI_SHORT_OK) xfer->flags.short_frames_ok = 1; else xfer->flags.short_frames_ok = 0; if (fs_ep.flags & USB_FS_FLAG_FORCE_SHORT) xfer->flags.force_short_xfer = 1; else xfer->flags.force_short_xfer = 0; if (fs_ep.flags & USB_FS_FLAG_CLEAR_STALL) usbd_xfer_set_stall(xfer); else xfer->flags.stall_pipe = 0; for (; n != xfer->nframes; n++) { error = copyin(fs_ep.pLength + n, &length, sizeof(length)); if (error) { break; } usbd_xfer_set_frame_len(xfer, n, length); if (length > rem) { xfer->error = USB_ERR_INVAL; goto complete; } rem -= length; if (!isread) { /* we need to know the source buffer */ error = copyin(fs_ep.ppBuffer + n, &uaddr, sizeof(uaddr)); if (error) { break; } if (xfer->flags_int.isochronous_xfr) { /* get kernel buffer address */ kaddr = xfer->frbuffers[0].buffer; kaddr = USB_ADD_BYTES(kaddr, offset); } else { /* set current frame offset */ usbd_xfer_set_frame_offset(xfer, offset, n); /* get kernel buffer address */ kaddr = xfer->frbuffers[n].buffer; } /* move data */ error = copyin(uaddr, kaddr, length); if (error) { break; } } offset += length; } return (error); complete: mtx_lock(f->priv_mtx); ugen_fs_set_complete(f, ep_index); mtx_unlock(f->priv_mtx); return (0); } static int ugen_fs_copy_out(struct usb_fifo *f, uint8_t ep_index) { struct usb_device_request *req; struct usb_xfer *xfer; struct usb_fs_endpoint fs_ep; struct usb_fs_endpoint *fs_ep_uptr; /* userland ptr */ void *uaddr; /* userland ptr */ void *kaddr; usb_frlength_t offset; usb_frlength_t rem; usb_frcount_t n; uint32_t length; uint32_t temp; int error; uint8_t isread; if (ep_index >= f->fs_ep_max) return (EINVAL); xfer = f->fs_xfer[ep_index]; if (xfer == NULL) return (EINVAL); mtx_lock(f->priv_mtx); if (usbd_transfer_pending(xfer)) { mtx_unlock(f->priv_mtx); return (EBUSY); /* should not happen */ } mtx_unlock(f->priv_mtx); fs_ep_uptr = f->fs_ep_ptr + ep_index; error = copyin(fs_ep_uptr, &fs_ep, sizeof(fs_ep)); if (error) { return (error); } fs_ep.status = xfer->error; fs_ep.aFrames = xfer->aframes; fs_ep.isoc_time_complete = xfer->isoc_time_complete; if (xfer->error) { goto complete; } if (xfer->flags_int.control_xfr) { req = xfer->frbuffers[0].buffer; /* Host mode only ! */ if ((req->bmRequestType & (UT_READ | UT_WRITE)) == UT_READ) { isread = 1; } else { isread = 0; } if (xfer->nframes == 0) n = 0; /* should never happen */ else n = 1; } else { /* Device and Host mode */ if (USB_GET_DATA_ISREAD(xfer)) { isread = 1; } else { isread = 0; } n = 0; } /* Update lengths and copy out data */ rem = usbd_xfer_max_len(xfer); offset = 0; for (; n != xfer->nframes; n++) { /* get initial length into "temp" */ error = copyin(fs_ep.pLength + n, &temp, sizeof(temp)); if (error) { return (error); } if (temp > rem) { /* the userland length has been corrupted */ DPRINTF("corrupt userland length " "%u > %u\n", temp, rem); fs_ep.status = USB_ERR_INVAL; goto complete; } rem -= temp; /* get actual transfer length */ length = xfer->frlengths[n]; if (length > temp) { /* data overflow */ fs_ep.status = USB_ERR_INVAL; DPRINTF("data overflow %u > %u\n", length, temp); goto complete; } if (isread) { /* we need to know the destination buffer */ error = copyin(fs_ep.ppBuffer + n, &uaddr, sizeof(uaddr)); if (error) { return (error); } if (xfer->flags_int.isochronous_xfr) { /* only one frame buffer */ kaddr = USB_ADD_BYTES( xfer->frbuffers[0].buffer, offset); } else { /* multiple frame buffers */ kaddr = xfer->frbuffers[n].buffer; } /* move data */ error = copyout(kaddr, uaddr, length); if (error) { return (error); } } /* * Update offset according to initial length, which is * needed by isochronous transfers! */ offset += temp; /* update length */ error = copyout(&length, fs_ep.pLength + n, sizeof(length)); if (error) { return (error); } } complete: /* update "aFrames" */ error = copyout(&fs_ep.aFrames, &fs_ep_uptr->aFrames, sizeof(fs_ep.aFrames)); if (error) goto done; /* update "isoc_time_complete" */ error = copyout(&fs_ep.isoc_time_complete, &fs_ep_uptr->isoc_time_complete, sizeof(fs_ep.isoc_time_complete)); if (error) goto done; /* update "status" */ error = copyout(&fs_ep.status, &fs_ep_uptr->status, sizeof(fs_ep.status)); done: return (error); } static uint8_t ugen_fifo_in_use(struct usb_fifo *f, int fflags) { struct usb_fifo *f_rx; struct usb_fifo *f_tx; f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX]; f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX]; if ((fflags & FREAD) && f_rx && (f_rx->xfer[0] || f_rx->xfer[1])) { return (1); /* RX FIFO in use */ } if ((fflags & FWRITE) && f_tx && (f_tx->xfer[0] || f_tx->xfer[1])) { return (1); /* TX FIFO in use */ } return (0); /* not in use */ } static int ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) { struct usb_config usb_config[1]; struct usb_device_request req; union { struct usb_fs_complete *pcomp; struct usb_fs_start *pstart; struct usb_fs_stop *pstop; struct usb_fs_open *popen; struct usb_fs_open_stream *popen_stream; struct usb_fs_close *pclose; struct usb_fs_clear_stall_sync *pstall; void *addr; } u; struct usb_endpoint *ep; struct usb_endpoint_descriptor *ed; struct usb_xfer *xfer; int error = 0; uint8_t iface_index; uint8_t isread; uint8_t ep_index; uint8_t pre_scale; u.addr = addr; DPRINTFN(6, "cmd=0x%08lx\n", cmd); switch (cmd) { case USB_FS_COMPLETE: mtx_lock(f->priv_mtx); error = ugen_fs_get_complete(f, &ep_index); mtx_unlock(f->priv_mtx); if (error) { error = EBUSY; break; } u.pcomp->ep_index = ep_index; error = ugen_fs_copy_out(f, u.pcomp->ep_index); break; case USB_FS_START: error = ugen_fs_copy_in(f, u.pstart->ep_index); if (error) break; mtx_lock(f->priv_mtx); xfer = f->fs_xfer[u.pstart->ep_index]; usbd_transfer_start(xfer); mtx_unlock(f->priv_mtx); break; case USB_FS_STOP: if (u.pstop->ep_index >= f->fs_ep_max) { error = EINVAL; break; } mtx_lock(f->priv_mtx); xfer = f->fs_xfer[u.pstart->ep_index]; if (usbd_transfer_pending(xfer)) { usbd_transfer_stop(xfer); /* * Check if the USB transfer was stopped * before it was even started. Else a cancel * callback will be pending. */ if (!xfer->flags_int.transferring) { ugen_fs_set_complete(xfer->priv_sc, USB_P2U(xfer->priv_fifo)); } } mtx_unlock(f->priv_mtx); break; case USB_FS_OPEN: case USB_FS_OPEN_STREAM: if (u.popen->ep_index >= f->fs_ep_max) { error = EINVAL; break; } if (f->fs_xfer[u.popen->ep_index] != NULL) { error = EBUSY; break; } if (u.popen->max_bufsize > USB_FS_MAX_BUFSIZE) { u.popen->max_bufsize = USB_FS_MAX_BUFSIZE; } if (u.popen->max_frames & USB_FS_MAX_FRAMES_PRE_SCALE) { pre_scale = 1; u.popen->max_frames &= ~USB_FS_MAX_FRAMES_PRE_SCALE; } else { pre_scale = 0; } if (u.popen->max_frames > USB_FS_MAX_FRAMES) { u.popen->max_frames = USB_FS_MAX_FRAMES; break; } if (u.popen->max_frames == 0) { error = EINVAL; break; } ep = usbd_get_ep_by_addr(f->udev, u.popen->ep_no); if (ep == NULL) { error = EINVAL; break; } ed = ep->edesc; if (ed == NULL) { error = ENXIO; break; } iface_index = ep->iface_index; memset(usb_config, 0, sizeof(usb_config)); usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; usb_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN); usb_config[0].interval = USB_DEFAULT_INTERVAL; usb_config[0].flags.proxy_buffer = 1; if (pre_scale != 0) usb_config[0].flags.pre_scale_frames = 1; usb_config[0].callback = &ugen_ctrl_fs_callback; usb_config[0].timeout = 0; /* no timeout */ usb_config[0].frames = u.popen->max_frames; usb_config[0].bufsize = u.popen->max_bufsize; usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ if (cmd == USB_FS_OPEN_STREAM) usb_config[0].stream_id = u.popen_stream->stream_id; if (usb_config[0].type == UE_CONTROL) { if (f->udev->flags.usb_mode != USB_MODE_HOST) { error = EINVAL; break; } } else { isread = ((usb_config[0].endpoint & (UE_DIR_IN | UE_DIR_OUT)) == UE_DIR_IN); if (f->udev->flags.usb_mode != USB_MODE_HOST) { isread = !isread; } /* check permissions */ if (isread) { if (!(fflags & FREAD)) { error = EPERM; break; } } else { if (!(fflags & FWRITE)) { error = EPERM; break; } } } error = usbd_transfer_setup(f->udev, &iface_index, f->fs_xfer + u.popen->ep_index, usb_config, 1, f, f->priv_mtx); if (error == 0) { /* update maximums */ u.popen->max_packet_length = f->fs_xfer[u.popen->ep_index]->max_frame_size; u.popen->max_bufsize = f->fs_xfer[u.popen->ep_index]->max_data_length; /* update number of frames */ u.popen->max_frames = f->fs_xfer[u.popen->ep_index]->nframes; /* store index of endpoint */ f->fs_xfer[u.popen->ep_index]->priv_fifo = ((uint8_t *)0) + u.popen->ep_index; } else { error = ENOMEM; } break; case USB_FS_CLOSE: if (u.pclose->ep_index >= f->fs_ep_max) { error = EINVAL; break; } if (f->fs_xfer[u.pclose->ep_index] == NULL) { error = EINVAL; break; } usbd_transfer_unsetup(f->fs_xfer + u.pclose->ep_index, 1); break; case USB_FS_CLEAR_STALL_SYNC: if (u.pstall->ep_index >= f->fs_ep_max) { error = EINVAL; break; } if (f->fs_xfer[u.pstall->ep_index] == NULL) { error = EINVAL; break; } if (f->udev->flags.usb_mode != USB_MODE_HOST) { error = EINVAL; break; } mtx_lock(f->priv_mtx); error = usbd_transfer_pending(f->fs_xfer[u.pstall->ep_index]); mtx_unlock(f->priv_mtx); if (error) { return (EBUSY); } ep = f->fs_xfer[u.pstall->ep_index]->endpoint; /* setup a clear-stall packet */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); req.wIndex[0] = ep->edesc->bEndpointAddress; req.wIndex[1] = 0; USETW(req.wLength, 0); error = usbd_do_request(f->udev, NULL, &req, NULL); if (error == 0) { usbd_clear_data_toggle(f->udev, ep); } else { error = ENXIO; } break; default: error = ENOIOCTL; break; } DPRINTFN(6, "error=%d\n", error); return (error); } static int ugen_set_short_xfer(struct usb_fifo *f, void *addr) { uint8_t t; if (*(int *)addr) t = 1; else t = 0; if (f->flag_short == t) { /* same value like before - accept */ return (0); } if (f->xfer[0] || f->xfer[1]) { /* cannot change this during transfer */ return (EBUSY); } f->flag_short = t; return (0); } static int ugen_set_timeout(struct usb_fifo *f, void *addr) { f->timeout = *(int *)addr; if (f->timeout > 65535) { /* limit user input */ f->timeout = 65535; } return (0); } static int ugen_get_frame_size(struct usb_fifo *f, void *addr) { if (f->xfer[0]) { *(int *)addr = f->xfer[0]->max_frame_size; } else { return (EINVAL); } return (0); } static int ugen_set_buffer_size(struct usb_fifo *f, void *addr) { usb_frlength_t t; if (*(int *)addr < 0) t = 0; /* use "wMaxPacketSize" */ else if (*(int *)addr < (256 * 1024)) t = *(int *)addr; else t = 256 * 1024; if (f->bufsize == t) { /* same value like before - accept */ return (0); } if (f->xfer[0] || f->xfer[1]) { /* cannot change this during transfer */ return (EBUSY); } f->bufsize = t; return (0); } static int ugen_get_buffer_size(struct usb_fifo *f, void *addr) { *(int *)addr = f->bufsize; return (0); } static int ugen_get_iface_desc(struct usb_fifo *f, struct usb_interface_descriptor *idesc) { struct usb_interface *iface; iface = usbd_get_iface(f->udev, f->iface_index); if (iface && iface->idesc) { *idesc = *(iface->idesc); } else { return (EIO); } return (0); } static int ugen_get_endpoint_desc(struct usb_fifo *f, struct usb_endpoint_descriptor *ed) { struct usb_endpoint *ep; ep = usb_fifo_softc(f); if (ep && ep->edesc) { *ed = *ep->edesc; } else { return (EINVAL); } return (0); } static int ugen_set_power_mode(struct usb_fifo *f, int mode) { struct usb_device *udev = f->udev; int err; uint8_t old_mode; if ((udev == NULL) || (udev->parent_hub == NULL)) { return (EINVAL); } err = priv_check(curthread, PRIV_DRIVER); if (err) return (err); /* get old power mode */ old_mode = udev->power_mode; /* if no change, then just return */ if (old_mode == mode) return (0); switch (mode) { case USB_POWER_MODE_OFF: if (udev->flags.usb_mode == USB_MODE_HOST && udev->re_enumerate_wait == USB_RE_ENUM_DONE) { udev->re_enumerate_wait = USB_RE_ENUM_PWR_OFF; } /* set power mode will wake up the explore thread */ break; case USB_POWER_MODE_ON: case USB_POWER_MODE_SAVE: break; case USB_POWER_MODE_RESUME: #if USB_HAVE_POWERD /* let USB-powerd handle resume */ USB_BUS_LOCK(udev->bus); udev->pwr_save.write_refs++; udev->pwr_save.last_xfer_time = ticks; USB_BUS_UNLOCK(udev->bus); /* set new power mode */ usbd_set_power_mode(udev, USB_POWER_MODE_SAVE); /* wait for resume to complete */ usb_pause_mtx(NULL, hz / 4); /* clear write reference */ USB_BUS_LOCK(udev->bus); udev->pwr_save.write_refs--; USB_BUS_UNLOCK(udev->bus); #endif mode = USB_POWER_MODE_SAVE; break; case USB_POWER_MODE_SUSPEND: #if USB_HAVE_POWERD /* let USB-powerd handle suspend */ USB_BUS_LOCK(udev->bus); udev->pwr_save.last_xfer_time = ticks - (256 * hz); USB_BUS_UNLOCK(udev->bus); #endif mode = USB_POWER_MODE_SAVE; break; default: return (EINVAL); } if (err) return (ENXIO); /* I/O failure */ /* if we are powered off we need to re-enumerate first */ if (old_mode == USB_POWER_MODE_OFF) { if (udev->flags.usb_mode == USB_MODE_HOST && udev->re_enumerate_wait == USB_RE_ENUM_DONE) { udev->re_enumerate_wait = USB_RE_ENUM_START; } /* set power mode will wake up the explore thread */ } /* set new power mode */ usbd_set_power_mode(udev, mode); return (0); /* success */ } static int ugen_get_power_mode(struct usb_fifo *f) { struct usb_device *udev = f->udev; if (udev == NULL) return (USB_POWER_MODE_ON); return (udev->power_mode); } static int ugen_get_port_path(struct usb_fifo *f, struct usb_device_port_path *dpp) { struct usb_device *udev = f->udev; struct usb_device *next; unsigned int nlevel = 0; if (udev == NULL) goto error; dpp->udp_bus = device_get_unit(udev->bus->bdev); dpp->udp_index = udev->device_index; /* count port levels */ next = udev; while (next->parent_hub != NULL) { nlevel++; next = next->parent_hub; } /* check if too many levels */ if (nlevel > USB_DEVICE_PORT_PATH_MAX) goto error; /* store total level of ports */ dpp->udp_port_level = nlevel; /* store port index array */ next = udev; while (next->parent_hub != NULL) { dpp->udp_port_no[--nlevel] = next->port_no; next = next->parent_hub; } return (0); /* success */ error: return (EINVAL); /* failure */ } static int ugen_get_power_usage(struct usb_fifo *f) { struct usb_device *udev = f->udev; if (udev == NULL) return (0); return (udev->power); } static int ugen_do_port_feature(struct usb_fifo *f, uint8_t port_no, uint8_t set, uint16_t feature) { struct usb_device *udev = f->udev; struct usb_hub *hub; int err; err = priv_check(curthread, PRIV_DRIVER); if (err) { return (err); } if (port_no == 0) { return (EINVAL); } if ((udev == NULL) || (udev->hub == NULL)) { return (EINVAL); } hub = udev->hub; if (port_no > hub->nports) { return (EINVAL); } if (set) err = usbd_req_set_port_feature(udev, NULL, port_no, feature); else err = usbd_req_clear_port_feature(udev, NULL, port_no, feature); if (err) return (ENXIO); /* failure */ return (0); /* success */ } static int ugen_iface_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) { struct usb_fifo *f_rx; struct usb_fifo *f_tx; int error = 0; f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX]; f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX]; switch (cmd) { case USB_SET_RX_SHORT_XFER: if (fflags & FREAD) { error = ugen_set_short_xfer(f_rx, addr); } else { error = EINVAL; } break; case USB_SET_TX_FORCE_SHORT: if (fflags & FWRITE) { error = ugen_set_short_xfer(f_tx, addr); } else { error = EINVAL; } break; case USB_SET_RX_TIMEOUT: if (fflags & FREAD) { error = ugen_set_timeout(f_rx, addr); } else { error = EINVAL; } break; case USB_SET_TX_TIMEOUT: if (fflags & FWRITE) { error = ugen_set_timeout(f_tx, addr); } else { error = EINVAL; } break; case USB_GET_RX_FRAME_SIZE: if (fflags & FREAD) { error = ugen_get_frame_size(f_rx, addr); } else { error = EINVAL; } break; case USB_GET_TX_FRAME_SIZE: if (fflags & FWRITE) { error = ugen_get_frame_size(f_tx, addr); } else { error = EINVAL; } break; case USB_SET_RX_BUFFER_SIZE: if (fflags & FREAD) { error = ugen_set_buffer_size(f_rx, addr); } else { error = EINVAL; } break; case USB_SET_TX_BUFFER_SIZE: if (fflags & FWRITE) { error = ugen_set_buffer_size(f_tx, addr); } else { error = EINVAL; } break; case USB_GET_RX_BUFFER_SIZE: if (fflags & FREAD) { error = ugen_get_buffer_size(f_rx, addr); } else { error = EINVAL; } break; case USB_GET_TX_BUFFER_SIZE: if (fflags & FWRITE) { error = ugen_get_buffer_size(f_tx, addr); } else { error = EINVAL; } break; case USB_GET_RX_INTERFACE_DESC: if (fflags & FREAD) { error = ugen_get_iface_desc(f_rx, addr); } else { error = EINVAL; } break; case USB_GET_TX_INTERFACE_DESC: if (fflags & FWRITE) { error = ugen_get_iface_desc(f_tx, addr); } else { error = EINVAL; } break; case USB_GET_RX_ENDPOINT_DESC: if (fflags & FREAD) { error = ugen_get_endpoint_desc(f_rx, addr); } else { error = EINVAL; } break; case USB_GET_TX_ENDPOINT_DESC: if (fflags & FWRITE) { error = ugen_get_endpoint_desc(f_tx, addr); } else { error = EINVAL; } break; case USB_SET_RX_STALL_FLAG: if ((fflags & FREAD) && (*(int *)addr)) { f_rx->flag_stall = 1; } break; case USB_SET_TX_STALL_FLAG: if ((fflags & FWRITE) && (*(int *)addr)) { f_tx->flag_stall = 1; } break; default: error = ENOIOCTL; break; } return (error); } static int ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags) { union { struct usb_interface_descriptor *idesc; struct usb_alt_interface *ai; struct usb_device_descriptor *ddesc; struct usb_config_descriptor *cdesc; struct usb_device_stats *stat; struct usb_fs_init *pinit; struct usb_fs_uninit *puninit; struct usb_device_port_path *dpp; uint32_t *ptime; void *addr; int *pint; } u; struct usb_device_descriptor *dtemp; struct usb_config_descriptor *ctemp; struct usb_interface *iface; int error = 0; uint8_t n; u.addr = addr; DPRINTFN(6, "cmd=0x%08lx\n", cmd); switch (cmd) { case USB_DISCOVER: usb_needs_explore_all(); break; case USB_SETDEBUG: if (!(fflags & FWRITE)) { error = EPERM; break; } usb_debug = *(int *)addr; break; case USB_GET_CONFIG: *(int *)addr = f->udev->curr_config_index; break; case USB_SET_CONFIG: if (!(fflags & FWRITE)) { error = EPERM; break; } error = ugen_set_config(f, *(int *)addr); break; case USB_GET_ALTINTERFACE: iface = usbd_get_iface(f->udev, u.ai->uai_interface_index); if (iface && iface->idesc) { u.ai->uai_alt_index = iface->alt_index; } else { error = EINVAL; } break; case USB_SET_ALTINTERFACE: if (!(fflags & FWRITE)) { error = EPERM; break; } error = ugen_set_interface(f, u.ai->uai_interface_index, u.ai->uai_alt_index); break; case USB_GET_DEVICE_DESC: dtemp = usbd_get_device_descriptor(f->udev); if (!dtemp) { error = EIO; break; } *u.ddesc = *dtemp; break; case USB_GET_CONFIG_DESC: ctemp = usbd_get_config_descriptor(f->udev); if (!ctemp) { error = EIO; break; } *u.cdesc = *ctemp; break; case USB_GET_FULL_DESC: error = ugen_get_cdesc(f, addr); break; case USB_GET_STRING_DESC: error = ugen_get_sdesc(f, addr); break; case USB_GET_IFACE_DRIVER: error = ugen_get_iface_driver(f, addr); break; case USB_REQUEST: case USB_DO_REQUEST: if (!(fflags & FWRITE)) { error = EPERM; break; } error = ugen_do_request(f, addr); break; case USB_DEVICEINFO: case USB_GET_DEVICEINFO: error = usb_gen_fill_deviceinfo(f, addr); break; case USB_DEVICESTATS: for (n = 0; n != 4; n++) { u.stat->uds_requests_fail[n] = f->udev->bus->stats_err.uds_requests[n]; u.stat->uds_requests_ok[n] = f->udev->bus->stats_ok.uds_requests[n]; } break; case USB_DEVICEENUMERATE: error = ugen_re_enumerate(f); break; case USB_GET_PLUGTIME: *u.ptime = f->udev->plugtime; break; case USB_CLAIM_INTERFACE: case USB_RELEASE_INTERFACE: /* TODO */ break; case USB_IFACE_DRIVER_ACTIVE: n = *u.pint & 0xFF; iface = usbd_get_iface(f->udev, n); if (iface && iface->subdev) error = 0; else error = ENXIO; break; case USB_IFACE_DRIVER_DETACH: error = priv_check(curthread, PRIV_DRIVER); if (error) break; n = *u.pint & 0xFF; if (n == USB_IFACE_INDEX_ANY) { error = EINVAL; break; } /* * Detach the currently attached driver. */ usb_detach_device(f->udev, n, 0); /* * Set parent to self, this should keep attach away * until the next set configuration event. */ usbd_set_parent_iface(f->udev, n, n); break; case USB_SET_POWER_MODE: error = ugen_set_power_mode(f, *u.pint); break; case USB_GET_POWER_MODE: *u.pint = ugen_get_power_mode(f); break; case USB_GET_DEV_PORT_PATH: error = ugen_get_port_path(f, u.dpp); break; case USB_GET_POWER_USAGE: *u.pint = ugen_get_power_usage(f); break; case USB_SET_PORT_ENABLE: error = ugen_do_port_feature(f, *u.pint, 1, UHF_PORT_ENABLE); break; case USB_SET_PORT_DISABLE: error = ugen_do_port_feature(f, *u.pint, 0, UHF_PORT_ENABLE); break; case USB_FS_INIT: /* verify input parameters */ if (u.pinit->pEndpoints == NULL) { error = EINVAL; break; } if (u.pinit->ep_index_max > 127) { error = EINVAL; break; } if (u.pinit->ep_index_max == 0) { error = EINVAL; break; } if (f->fs_xfer != NULL) { error = EBUSY; break; } if (f->dev_ep_index != 0) { error = EINVAL; break; } if (ugen_fifo_in_use(f, fflags)) { error = EBUSY; break; } error = usb_fifo_alloc_buffer(f, 1, u.pinit->ep_index_max); if (error) { break; } f->fs_xfer = malloc(sizeof(f->fs_xfer[0]) * u.pinit->ep_index_max, M_USB, M_WAITOK | M_ZERO); if (f->fs_xfer == NULL) { usb_fifo_free_buffer(f); error = ENOMEM; break; } f->fs_ep_max = u.pinit->ep_index_max; f->fs_ep_ptr = u.pinit->pEndpoints; break; case USB_FS_UNINIT: if (u.puninit->dummy != 0) { error = EINVAL; break; } error = ugen_fs_uninit(f); break; default: mtx_lock(f->priv_mtx); error = ugen_iface_ioctl(f, cmd, addr, fflags); mtx_unlock(f->priv_mtx); break; } DPRINTFN(6, "error=%d\n", error); return (error); } static void ugen_ctrl_fs_callback(struct usb_xfer *xfer, usb_error_t error) { ; /* workaround for a bug in "indent" */ DPRINTF("st=%u alen=%u aframes=%u\n", USB_GET_STATE(xfer), xfer->actlen, xfer->aframes); switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: usbd_transfer_submit(xfer); break; default: ugen_fs_set_complete(xfer->priv_sc, USB_P2U(xfer->priv_fifo)); break; } } #endif /* USB_HAVE_UGEN */ Index: projects/clang390-import/sys/dev/usb/usb_request.c =================================================================== --- projects/clang390-import/sys/dev/usb/usb_request.c (revision 305430) +++ projects/clang390-import/sys/dev/usb/usb_request.c (revision 305431) @@ -1,2291 +1,2282 @@ /* $FreeBSD$ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. * Copyright (c) 1998 Lennart Augustsson. All rights reserved. * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ static int usb_no_cs_fail; SYSCTL_INT(_hw_usb, OID_AUTO, no_cs_fail, CTLFLAG_RWTUN, &usb_no_cs_fail, 0, "USB clear stall failures are ignored, if set"); static int usb_full_ddesc; SYSCTL_INT(_hw_usb, OID_AUTO, full_ddesc, CTLFLAG_RWTUN, &usb_full_ddesc, 0, "USB always read complete device descriptor, if set"); #ifdef USB_DEBUG #ifdef USB_REQ_DEBUG /* The following structures are used in connection to fault injection. */ struct usb_ctrl_debug { int bus_index; /* target bus */ int dev_index; /* target address */ int ds_fail; /* fail data stage */ int ss_fail; /* fail status stage */ int ds_delay; /* data stage delay in ms */ int ss_delay; /* status stage delay in ms */ int bmRequestType_value; int bRequest_value; }; struct usb_ctrl_debug_bits { uint16_t ds_delay; uint16_t ss_delay; uint8_t ds_fail:1; uint8_t ss_fail:1; uint8_t enabled:1; }; /* The default is to disable fault injection. */ static struct usb_ctrl_debug usb_ctrl_debug = { .bus_index = -1, .dev_index = -1, .bmRequestType_value = -1, .bRequest_value = -1, }; SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_bus_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.bus_index, 0, "USB controller index to fail"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_dev_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.dev_index, 0, "USB device address to fail"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.ds_fail, 0, "USB fail data stage"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.ss_fail, 0, "USB fail status stage"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_delay, CTLFLAG_RWTUN, &usb_ctrl_debug.ds_delay, 0, "USB data stage delay in ms"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_delay, CTLFLAG_RWTUN, &usb_ctrl_debug.ss_delay, 0, "USB status stage delay in ms"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rt_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.bmRequestType_value, 0, "USB bmRequestType to fail"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rv_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.bRequest_value, 0, "USB bRequest to fail"); /*------------------------------------------------------------------------* * usbd_get_debug_bits * * This function is only useful in USB host mode. *------------------------------------------------------------------------*/ static void usbd_get_debug_bits(struct usb_device *udev, struct usb_device_request *req, struct usb_ctrl_debug_bits *dbg) { int temp; memset(dbg, 0, sizeof(*dbg)); /* Compute data stage delay */ temp = usb_ctrl_debug.ds_delay; if (temp < 0) temp = 0; else if (temp > (16*1024)) temp = (16*1024); dbg->ds_delay = temp; /* Compute status stage delay */ temp = usb_ctrl_debug.ss_delay; if (temp < 0) temp = 0; else if (temp > (16*1024)) temp = (16*1024); dbg->ss_delay = temp; /* Check if this control request should be failed */ if (usbd_get_bus_index(udev) != usb_ctrl_debug.bus_index) return; if (usbd_get_device_index(udev) != usb_ctrl_debug.dev_index) return; temp = usb_ctrl_debug.bmRequestType_value; if ((temp != req->bmRequestType) && (temp >= 0) && (temp <= 255)) return; temp = usb_ctrl_debug.bRequest_value; if ((temp != req->bRequest) && (temp >= 0) && (temp <= 255)) return; temp = usb_ctrl_debug.ds_fail; if (temp) dbg->ds_fail = 1; temp = usb_ctrl_debug.ss_fail; if (temp) dbg->ss_fail = 1; dbg->enabled = 1; } #endif /* USB_REQ_DEBUG */ #endif /* USB_DEBUG */ /*------------------------------------------------------------------------* * usbd_do_request_callback * * This function is the USB callback for generic USB Host control * transfers. *------------------------------------------------------------------------*/ void usbd_do_request_callback(struct usb_xfer *xfer, usb_error_t error) { ; /* workaround for a bug in "indent" */ DPRINTF("st=%u\n", USB_GET_STATE(xfer)); switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: usbd_transfer_submit(xfer); break; default: cv_signal(&xfer->xroot->udev->ctrlreq_cv); break; } } /*------------------------------------------------------------------------* * usb_do_clear_stall_callback * * This function is the USB callback for generic clear stall requests. *------------------------------------------------------------------------*/ void usb_do_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_device_request req; struct usb_device *udev; struct usb_endpoint *ep; struct usb_endpoint *ep_end; struct usb_endpoint *ep_first; usb_stream_t x; uint8_t to; udev = xfer->xroot->udev; USB_BUS_LOCK(udev->bus); /* round robin endpoint clear stall */ ep = udev->ep_curr; ep_end = udev->endpoints + udev->endpoints_max; ep_first = udev->endpoints; to = udev->endpoints_max; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: tr_transferred: /* reset error counter */ udev->clear_stall_errors = 0; if (ep == NULL) goto tr_setup; /* device was unconfigured */ if (ep->edesc && ep->is_stalled) { ep->toggle_next = 0; ep->is_stalled = 0; /* some hardware needs a callback to clear the data toggle */ usbd_clear_stall_locked(udev, ep); for (x = 0; x != USB_MAX_EP_STREAMS; x++) { /* start the current or next transfer, if any */ usb_command_wrapper(&ep->endpoint_q[x], ep->endpoint_q[x].curr); } } ep++; case USB_ST_SETUP: tr_setup: if (to == 0) break; /* no endpoints - nothing to do */ if ((ep < ep_first) || (ep >= ep_end)) ep = ep_first; /* endpoint wrapped around */ if (ep->edesc && ep->is_stalled) { /* setup a clear-stall packet */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); req.wIndex[0] = ep->edesc->bEndpointAddress; req.wIndex[1] = 0; USETW(req.wLength, 0); /* copy in the transfer */ usbd_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); /* set length */ usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); xfer->nframes = 1; USB_BUS_UNLOCK(udev->bus); usbd_transfer_submit(xfer); USB_BUS_LOCK(udev->bus); break; } ep++; to--; goto tr_setup; default: if (error == USB_ERR_CANCELLED) break; DPRINTF("Clear stall failed.\n"); /* * Some VMs like VirtualBox always return failure on * clear-stall which we sometimes should just ignore. */ if (usb_no_cs_fail) goto tr_transferred; if (udev->clear_stall_errors == USB_CS_RESET_LIMIT) goto tr_setup; if (error == USB_ERR_TIMEOUT) { udev->clear_stall_errors = USB_CS_RESET_LIMIT; DPRINTF("Trying to re-enumerate.\n"); usbd_start_re_enumerate(udev); } else { udev->clear_stall_errors++; if (udev->clear_stall_errors == USB_CS_RESET_LIMIT) { DPRINTF("Trying to re-enumerate.\n"); usbd_start_re_enumerate(udev); } } goto tr_setup; } /* store current endpoint */ udev->ep_curr = ep; USB_BUS_UNLOCK(udev->bus); } static usb_handle_req_t * usbd_get_hr_func(struct usb_device *udev) { /* figure out if there is a Handle Request function */ if (udev->flags.usb_mode == USB_MODE_DEVICE) return (usb_temp_get_desc_p); else if (udev->parent_hub == NULL) return (udev->bus->methods->roothub_exec); else return (NULL); } /*------------------------------------------------------------------------* * usbd_do_request_flags and usbd_do_request * * Description of arguments passed to these functions: * * "udev" - this is the "usb_device" structure pointer on which the * request should be performed. It is possible to call this function * in both Host Side mode and Device Side mode. * * "mtx" - if this argument is non-NULL the mutex pointed to by it * will get dropped and picked up during the execution of this * function, hence this function sometimes needs to sleep. If this * argument is NULL it has no effect. * * "req" - this argument must always be non-NULL and points to an * 8-byte structure holding the USB request to be done. The USB * request structure has a bit telling the direction of the USB * request, if it is a read or a write. * * "data" - if the "wLength" part of the structure pointed to by "req" * is non-zero this argument must point to a valid kernel buffer which * can hold at least "wLength" bytes. If "wLength" is zero "data" can * be NULL. * * "flags" - here is a list of valid flags: * * o USB_SHORT_XFER_OK: allows the data transfer to be shorter than * specified * * o USB_DELAY_STATUS_STAGE: allows the status stage to be performed * at a later point in time. This is tunable by the "hw.usb.ss_delay" * sysctl. This flag is mostly useful for debugging. * * o USB_USER_DATA_PTR: treat the "data" pointer like a userland * pointer. * * "actlen" - if non-NULL the actual transfer length will be stored in * the 16-bit unsigned integer pointed to by "actlen". This * information is mostly useful when the "USB_SHORT_XFER_OK" flag is * used. * * "timeout" - gives the timeout for the control transfer in * milliseconds. A "timeout" value less than 50 milliseconds is * treated like a 50 millisecond timeout. A "timeout" value greater * than 30 seconds is treated like a 30 second timeout. This USB stack * does not allow control requests without a timeout. * * NOTE: This function is thread safe. All calls to "usbd_do_request_flags" * will be serialized by the use of the USB device enumeration lock. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, struct usb_device_request *req, void *data, uint16_t flags, uint16_t *actlen, usb_timeout_t timeout) { #ifdef USB_REQ_DEBUG struct usb_ctrl_debug_bits dbg; #endif usb_handle_req_t *hr_func; struct usb_xfer *xfer; const void *desc; int err = 0; usb_ticks_t start_ticks; usb_ticks_t delta_ticks; usb_ticks_t max_ticks; uint16_t length; uint16_t temp; uint16_t acttemp; uint8_t do_unlock; if (timeout < 50) { /* timeout is too small */ timeout = 50; } if (timeout > 30000) { /* timeout is too big */ timeout = 30000; } length = UGETW(req->wLength); DPRINTFN(5, "udev=%p bmRequestType=0x%02x bRequest=0x%02x " "wValue=0x%02x%02x wIndex=0x%02x%02x wLength=0x%02x%02x\n", udev, req->bmRequestType, req->bRequest, req->wValue[1], req->wValue[0], req->wIndex[1], req->wIndex[0], req->wLength[1], req->wLength[0]); /* Check if the device is still alive */ if (udev->state < USB_STATE_POWERED) { DPRINTF("usb device has gone\n"); return (USB_ERR_NOT_CONFIGURED); } /* * Set "actlen" to a known value in case the caller does not * check the return value: */ if (actlen) *actlen = 0; #if (USB_HAVE_USER_IO == 0) if (flags & USB_USER_DATA_PTR) return (USB_ERR_INVAL); #endif if ((mtx != NULL) && (mtx != &Giant)) { mtx_unlock(mtx); mtx_assert(mtx, MA_NOTOWNED); } /* - * Grab the USB device enumeration SX-lock serialization is - * achieved when multiple threads are involved: + * Serialize access to this function: */ - do_unlock = usbd_enum_lock(udev); + do_unlock = usbd_ctrl_lock(udev); - /* - * We need to allow suspend and resume at this point, else the - * control transfer will timeout if the device is suspended! - */ - usbd_sr_unlock(udev); - hr_func = usbd_get_hr_func(udev); if (hr_func != NULL) { DPRINTF("Handle Request function is set\n"); desc = NULL; temp = 0; if (!(req->bmRequestType & UT_READ)) { if (length != 0) { DPRINTFN(1, "The handle request function " "does not support writing data!\n"); err = USB_ERR_INVAL; goto done; } } /* The root HUB code needs the BUS lock locked */ USB_BUS_LOCK(udev->bus); err = (hr_func) (udev, req, &desc, &temp); USB_BUS_UNLOCK(udev->bus); if (err) goto done; if (length > temp) { if (!(flags & USB_SHORT_XFER_OK)) { err = USB_ERR_SHORT_XFER; goto done; } length = temp; } if (actlen) *actlen = length; if (length > 0) { #if USB_HAVE_USER_IO if (flags & USB_USER_DATA_PTR) { if (copyout(desc, data, length)) { err = USB_ERR_INVAL; goto done; } } else #endif memcpy(data, desc, length); } goto done; /* success */ } /* * Setup a new USB transfer or use the existing one, if any: */ usbd_ctrl_transfer_setup(udev); xfer = udev->ctrl_xfer[0]; if (xfer == NULL) { /* most likely out of memory */ err = USB_ERR_NOMEM; goto done; } #ifdef USB_REQ_DEBUG /* Get debug bits */ usbd_get_debug_bits(udev, req, &dbg); /* Check for fault injection */ if (dbg.enabled) flags |= USB_DELAY_STATUS_STAGE; #endif USB_XFER_LOCK(xfer); if (flags & USB_DELAY_STATUS_STAGE) xfer->flags.manual_status = 1; else xfer->flags.manual_status = 0; if (flags & USB_SHORT_XFER_OK) xfer->flags.short_xfer_ok = 1; else xfer->flags.short_xfer_ok = 0; xfer->timeout = timeout; start_ticks = ticks; max_ticks = USB_MS_TO_TICKS(timeout); usbd_copy_in(xfer->frbuffers, 0, req, sizeof(*req)); usbd_xfer_set_frame_len(xfer, 0, sizeof(*req)); while (1) { temp = length; if (temp > usbd_xfer_max_len(xfer)) { temp = usbd_xfer_max_len(xfer); } #ifdef USB_REQ_DEBUG if (xfer->flags.manual_status) { if (usbd_xfer_frame_len(xfer, 0) != 0) { /* Execute data stage separately */ temp = 0; } else if (temp > 0) { if (dbg.ds_fail) { err = USB_ERR_INVAL; break; } if (dbg.ds_delay > 0) { usb_pause_mtx( xfer->xroot->xfer_mtx, USB_MS_TO_TICKS(dbg.ds_delay)); /* make sure we don't time out */ start_ticks = ticks; } } } #endif usbd_xfer_set_frame_len(xfer, 1, temp); if (temp > 0) { if (!(req->bmRequestType & UT_READ)) { #if USB_HAVE_USER_IO if (flags & USB_USER_DATA_PTR) { USB_XFER_UNLOCK(xfer); err = usbd_copy_in_user(xfer->frbuffers + 1, 0, data, temp); USB_XFER_LOCK(xfer); if (err) { err = USB_ERR_INVAL; break; } } else #endif usbd_copy_in(xfer->frbuffers + 1, 0, data, temp); } usbd_xfer_set_frames(xfer, 2); } else { if (usbd_xfer_frame_len(xfer, 0) == 0) { if (xfer->flags.manual_status) { #ifdef USB_REQ_DEBUG if (dbg.ss_fail) { err = USB_ERR_INVAL; break; } if (dbg.ss_delay > 0) { usb_pause_mtx( xfer->xroot->xfer_mtx, USB_MS_TO_TICKS(dbg.ss_delay)); /* make sure we don't time out */ start_ticks = ticks; } #endif xfer->flags.manual_status = 0; } else { break; } } usbd_xfer_set_frames(xfer, 1); } usbd_transfer_start(xfer); while (usbd_transfer_pending(xfer)) { cv_wait(&udev->ctrlreq_cv, xfer->xroot->xfer_mtx); } err = xfer->error; if (err) { break; } /* get actual length of DATA stage */ if (xfer->aframes < 2) { acttemp = 0; } else { acttemp = usbd_xfer_frame_len(xfer, 1); } /* check for short packet */ if (temp > acttemp) { temp = acttemp; length = temp; } if (temp > 0) { if (req->bmRequestType & UT_READ) { #if USB_HAVE_USER_IO if (flags & USB_USER_DATA_PTR) { USB_XFER_UNLOCK(xfer); err = usbd_copy_out_user(xfer->frbuffers + 1, 0, data, temp); USB_XFER_LOCK(xfer); if (err) { err = USB_ERR_INVAL; break; } } else #endif usbd_copy_out(xfer->frbuffers + 1, 0, data, temp); } } /* * Clear "frlengths[0]" so that we don't send the setup * packet again: */ usbd_xfer_set_frame_len(xfer, 0, 0); /* update length and data pointer */ length -= temp; data = USB_ADD_BYTES(data, temp); if (actlen) { (*actlen) += temp; } /* check for timeout */ delta_ticks = ticks - start_ticks; if (delta_ticks > max_ticks) { if (!err) { err = USB_ERR_TIMEOUT; } } if (err) { break; } } if (err) { /* * Make sure that the control endpoint is no longer * blocked in case of a non-transfer related error: */ usbd_transfer_stop(xfer); } USB_XFER_UNLOCK(xfer); done: - usbd_sr_lock(udev); - if (do_unlock) - usbd_enum_unlock(udev); + usbd_ctrl_unlock(udev); if ((mtx != NULL) && (mtx != &Giant)) mtx_lock(mtx); switch (err) { case USB_ERR_NORMAL_COMPLETION: case USB_ERR_SHORT_XFER: case USB_ERR_STALLED: case USB_ERR_CANCELLED: break; default: DPRINTF("I/O error - waiting a bit for TT cleanup\n"); usb_pause_mtx(mtx, hz / 16); break; } return ((usb_error_t)err); } /*------------------------------------------------------------------------* * usbd_do_request_proc - factored out code * * This function is factored out code. It does basically the same like * usbd_do_request_flags, except it will check the status of the * passed process argument before doing the USB request. If the * process is draining the USB_ERR_IOERROR code will be returned. It * is assumed that the mutex associated with the process is locked * when calling this function. *------------------------------------------------------------------------*/ usb_error_t usbd_do_request_proc(struct usb_device *udev, struct usb_process *pproc, struct usb_device_request *req, void *data, uint16_t flags, uint16_t *actlen, usb_timeout_t timeout) { usb_error_t err; uint16_t len; /* get request data length */ len = UGETW(req->wLength); /* check if the device is being detached */ if (usb_proc_is_gone(pproc)) { err = USB_ERR_IOERROR; goto done; } /* forward the USB request */ err = usbd_do_request_flags(udev, pproc->up_mtx, req, data, flags, actlen, timeout); done: /* on failure we zero the data */ /* on short packet we zero the unused data */ if ((len != 0) && (req->bmRequestType & UE_DIR_IN)) { if (err) memset(data, 0, len); else if (actlen && *actlen != len) memset(((uint8_t *)data) + *actlen, 0, len - *actlen); } return (err); } /*------------------------------------------------------------------------* * usbd_req_reset_port * * This function will instruct a USB HUB to perform a reset sequence * on the specified port number. * * Returns: * 0: Success. The USB device should now be at address zero. * Else: Failure. No USB device is present and the USB port should be * disabled. *------------------------------------------------------------------------*/ usb_error_t usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) { struct usb_port_status ps; usb_error_t err; uint16_t n; uint16_t status; uint16_t change; DPRINTF("\n"); /* clear any leftover port reset changes first */ usbd_req_clear_port_feature( udev, mtx, port, UHF_C_PORT_RESET); /* assert port reset on the given port */ err = usbd_req_set_port_feature( udev, mtx, port, UHF_PORT_RESET); /* check for errors */ if (err) goto done; n = 0; while (1) { /* wait for the device to recover from reset */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_delay)); n += usb_port_reset_delay; err = usbd_req_get_port_status(udev, mtx, &ps, port); if (err) goto done; status = UGETW(ps.wPortStatus); change = UGETW(ps.wPortChange); /* if the device disappeared, just give up */ if (!(status & UPS_CURRENT_CONNECT_STATUS)) goto done; /* check if reset is complete */ if (change & UPS_C_PORT_RESET) break; /* * Some Virtual Machines like VirtualBox 4.x fail to * generate a port reset change event. Check if reset * is no longer asserted. */ if (!(status & UPS_RESET)) break; /* check for timeout */ if (n > 1000) { n = 0; break; } } /* clear port reset first */ err = usbd_req_clear_port_feature( udev, mtx, port, UHF_C_PORT_RESET); if (err) goto done; /* check for timeout */ if (n == 0) { err = USB_ERR_TIMEOUT; goto done; } /* wait for the device to recover from reset */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_recovery)); done: DPRINTFN(2, "port %d reset returning error=%s\n", port, usbd_errstr(err)); return (err); } /*------------------------------------------------------------------------* * usbd_req_warm_reset_port * * This function will instruct an USB HUB to perform a warm reset * sequence on the specified port number. This kind of reset is not * mandatory for LOW-, FULL- and HIGH-speed USB HUBs and is targeted * for SUPER-speed USB HUBs. * * Returns: * 0: Success. The USB device should now be available again. * Else: Failure. No USB device is present and the USB port should be * disabled. *------------------------------------------------------------------------*/ usb_error_t usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) { struct usb_port_status ps; usb_error_t err; uint16_t n; uint16_t status; uint16_t change; DPRINTF("\n"); err = usbd_req_get_port_status(udev, mtx, &ps, port); if (err) goto done; status = UGETW(ps.wPortStatus); switch (UPS_PORT_LINK_STATE_GET(status)) { case UPS_PORT_LS_U3: case UPS_PORT_LS_COMP_MODE: case UPS_PORT_LS_LOOPBACK: case UPS_PORT_LS_SS_INA: break; default: DPRINTF("Wrong state for warm reset\n"); return (0); } /* clear any leftover warm port reset changes first */ usbd_req_clear_port_feature(udev, mtx, port, UHF_C_BH_PORT_RESET); /* set warm port reset */ err = usbd_req_set_port_feature(udev, mtx, port, UHF_BH_PORT_RESET); if (err) goto done; n = 0; while (1) { /* wait for the device to recover from reset */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_delay)); n += usb_port_reset_delay; err = usbd_req_get_port_status(udev, mtx, &ps, port); if (err) goto done; status = UGETW(ps.wPortStatus); change = UGETW(ps.wPortChange); /* if the device disappeared, just give up */ if (!(status & UPS_CURRENT_CONNECT_STATUS)) goto done; /* check if reset is complete */ if (change & UPS_C_BH_PORT_RESET) break; /* check for timeout */ if (n > 1000) { n = 0; break; } } /* clear port reset first */ err = usbd_req_clear_port_feature( udev, mtx, port, UHF_C_BH_PORT_RESET); if (err) goto done; /* check for timeout */ if (n == 0) { err = USB_ERR_TIMEOUT; goto done; } /* wait for the device to recover from reset */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_recovery)); done: DPRINTFN(2, "port %d warm reset returning error=%s\n", port, usbd_errstr(err)); return (err); } /*------------------------------------------------------------------------* * usbd_req_get_desc * * This function can be used to retrieve USB descriptors. It contains * some additional logic like zeroing of missing descriptor bytes and * retrying an USB descriptor in case of failure. The "min_len" * argument specifies the minimum descriptor length. The "max_len" * argument specifies the maximum descriptor length. If the real * descriptor length is less than the minimum length the missing * byte(s) will be zeroed. The type field, the second byte of the USB * descriptor, will get forced to the correct type. If the "actlen" * pointer is non-NULL, the actual length of the transfer will get * stored in the 16-bit unsigned integer which it is pointing to. The * first byte of the descriptor will not get updated. If the "actlen" * pointer is NULL the first byte of the descriptor will get updated * to reflect the actual length instead. If "min_len" is not equal to * "max_len" then this function will try to retrive the beginning of * the descriptor and base the maximum length on the first byte of the * descriptor. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_desc(struct usb_device *udev, struct mtx *mtx, uint16_t *actlen, void *desc, uint16_t min_len, uint16_t max_len, uint16_t id, uint8_t type, uint8_t index, uint8_t retries) { struct usb_device_request req; uint8_t *buf; usb_error_t err; DPRINTFN(4, "id=%d, type=%d, index=%d, max_len=%d\n", id, type, index, max_len); req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, type, index); USETW(req.wIndex, id); while (1) { if ((min_len < 2) || (max_len < 2)) { err = USB_ERR_INVAL; goto done; } USETW(req.wLength, min_len); err = usbd_do_request_flags(udev, mtx, &req, desc, 0, NULL, 500 /* ms */); if (err) { if (!retries) { goto done; } retries--; usb_pause_mtx(mtx, hz / 5); continue; } buf = desc; if (min_len == max_len) { /* enforce correct length */ if ((buf[0] > min_len) && (actlen == NULL)) buf[0] = min_len; /* enforce correct type */ buf[1] = type; goto done; } /* range check */ if (max_len > buf[0]) { max_len = buf[0]; } /* zero minimum data */ while (min_len > max_len) { min_len--; buf[min_len] = 0; } /* set new minimum length */ min_len = max_len; } done: if (actlen != NULL) { if (err) *actlen = 0; else *actlen = min_len; } return (err); } /*------------------------------------------------------------------------* * usbd_req_get_string_any * * This function will return the string given by "string_index" * using the first language ID. The maximum length "len" includes * the terminating zero. The "len" argument should be twice as * big pluss 2 bytes, compared with the actual maximum string length ! * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_string_any(struct usb_device *udev, struct mtx *mtx, char *buf, uint16_t len, uint8_t string_index) { char *s; uint8_t *temp; uint16_t i; uint16_t n; uint16_t c; uint8_t swap; usb_error_t err; if (len == 0) { /* should not happen */ return (USB_ERR_NORMAL_COMPLETION); } if (string_index == 0) { /* this is the language table */ buf[0] = 0; return (USB_ERR_INVAL); } if (udev->flags.no_strings) { buf[0] = 0; return (USB_ERR_STALLED); } err = usbd_req_get_string_desc (udev, mtx, buf, len, udev->langid, string_index); if (err) { buf[0] = 0; return (err); } temp = (uint8_t *)buf; if (temp[0] < 2) { /* string length is too short */ buf[0] = 0; return (USB_ERR_INVAL); } /* reserve one byte for terminating zero */ len--; /* find maximum length */ s = buf; n = (temp[0] / 2) - 1; if (n > len) { n = len; } /* skip descriptor header */ temp += 2; /* reset swap state */ swap = 3; /* convert and filter */ for (i = 0; (i != n); i++) { c = UGETW(temp + (2 * i)); /* convert from Unicode, handle buggy strings */ if (((c & 0xff00) == 0) && (swap & 1)) { /* Little Endian, default */ *s = c; swap = 1; } else if (((c & 0x00ff) == 0) && (swap & 2)) { /* Big Endian */ *s = c >> 8; swap = 2; } else { /* silently skip bad character */ continue; } /* * Filter by default - We only allow alphanumerical * and a few more to avoid any problems with scripts * and daemons. */ if (isalpha(*s) || isdigit(*s) || *s == '-' || *s == '+' || *s == ' ' || *s == '.' || *s == ',') { /* allowed */ s++; } /* silently skip bad character */ } *s = 0; /* zero terminate resulting string */ return (USB_ERR_NORMAL_COMPLETION); } /*------------------------------------------------------------------------* * usbd_req_get_string_desc * * If you don't know the language ID, consider using * "usbd_req_get_string_any()". * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_string_desc(struct usb_device *udev, struct mtx *mtx, void *sdesc, uint16_t max_len, uint16_t lang_id, uint8_t string_index) { return (usbd_req_get_desc(udev, mtx, NULL, sdesc, 2, max_len, lang_id, UDESC_STRING, string_index, 0)); } /*------------------------------------------------------------------------* * usbd_req_get_config_desc_ptr * * This function is used in device side mode to retrieve the pointer * to the generated config descriptor. This saves allocating space for * an additional config descriptor when setting the configuration. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_descriptor_ptr(struct usb_device *udev, struct usb_config_descriptor **ppcd, uint16_t wValue) { struct usb_device_request req; usb_handle_req_t *hr_func; const void *ptr; uint16_t len; usb_error_t err; req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW(req.wValue, wValue); USETW(req.wIndex, 0); USETW(req.wLength, 0); ptr = NULL; len = 0; hr_func = usbd_get_hr_func(udev); if (hr_func == NULL) err = USB_ERR_INVAL; else { USB_BUS_LOCK(udev->bus); err = (hr_func) (udev, &req, &ptr, &len); USB_BUS_UNLOCK(udev->bus); } if (err) ptr = NULL; else if (ptr == NULL) err = USB_ERR_INVAL; *ppcd = __DECONST(struct usb_config_descriptor *, ptr); return (err); } /*------------------------------------------------------------------------* * usbd_req_get_config_desc * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_config_desc(struct usb_device *udev, struct mtx *mtx, struct usb_config_descriptor *d, uint8_t conf_index) { usb_error_t err; DPRINTFN(4, "confidx=%d\n", conf_index); err = usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), sizeof(*d), 0, UDESC_CONFIG, conf_index, 0); if (err) { goto done; } /* Extra sanity checking */ if (UGETW(d->wTotalLength) < (uint16_t)sizeof(*d)) { err = USB_ERR_INVAL; } done: return (err); } /*------------------------------------------------------------------------* * usbd_alloc_config_desc * * This function is used to allocate a zeroed configuration * descriptor. * * Returns: * NULL: Failure * Else: Success *------------------------------------------------------------------------*/ void * usbd_alloc_config_desc(struct usb_device *udev, uint32_t size) { if (size > USB_CONFIG_MAX) { DPRINTF("Configuration descriptor too big\n"); return (NULL); } #if (USB_HAVE_FIXED_CONFIG == 0) return (malloc(size, M_USBDEV, M_ZERO | M_WAITOK)); #else memset(udev->config_data, 0, sizeof(udev->config_data)); return (udev->config_data); #endif } /*------------------------------------------------------------------------* * usbd_alloc_config_desc * * This function is used to free a configuration descriptor. *------------------------------------------------------------------------*/ void usbd_free_config_desc(struct usb_device *udev, void *ptr) { #if (USB_HAVE_FIXED_CONFIG == 0) free(ptr, M_USBDEV); #endif } /*------------------------------------------------------------------------* * usbd_req_get_config_desc_full * * This function gets the complete USB configuration descriptor and * ensures that "wTotalLength" is correct. The returned configuration * descriptor is freed by calling "usbd_free_config_desc()". * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_config_desc_full(struct usb_device *udev, struct mtx *mtx, struct usb_config_descriptor **ppcd, uint8_t index) { struct usb_config_descriptor cd; struct usb_config_descriptor *cdesc; uint32_t len; usb_error_t err; DPRINTFN(4, "index=%d\n", index); *ppcd = NULL; err = usbd_req_get_config_desc(udev, mtx, &cd, index); if (err) return (err); /* get full descriptor */ len = UGETW(cd.wTotalLength); if (len < (uint32_t)sizeof(*cdesc)) { /* corrupt descriptor */ return (USB_ERR_INVAL); } else if (len > USB_CONFIG_MAX) { DPRINTF("Configuration descriptor was truncated\n"); len = USB_CONFIG_MAX; } cdesc = usbd_alloc_config_desc(udev, len); if (cdesc == NULL) return (USB_ERR_NOMEM); err = usbd_req_get_desc(udev, mtx, NULL, cdesc, len, len, 0, UDESC_CONFIG, index, 3); if (err) { usbd_free_config_desc(udev, cdesc); return (err); } /* make sure that the device is not fooling us: */ USETW(cdesc->wTotalLength, len); *ppcd = cdesc; return (0); /* success */ } /*------------------------------------------------------------------------* * usbd_req_get_device_desc * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_device_desc(struct usb_device *udev, struct mtx *mtx, struct usb_device_descriptor *d) { DPRINTFN(4, "\n"); return (usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), sizeof(*d), 0, UDESC_DEVICE, 0, 3)); } /*------------------------------------------------------------------------* * usbd_req_get_alt_interface_no * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_alt_interface_no(struct usb_device *udev, struct mtx *mtx, uint8_t *alt_iface_no, uint8_t iface_index) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) return (USB_ERR_INVAL); req.bmRequestType = UT_READ_INTERFACE; req.bRequest = UR_GET_INTERFACE; USETW(req.wValue, 0); req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, 1); return (usbd_do_request(udev, mtx, &req, alt_iface_no)); } /*------------------------------------------------------------------------* * usbd_req_set_alt_interface_no * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_alt_interface_no(struct usb_device *udev, struct mtx *mtx, uint8_t iface_index, uint8_t alt_no) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) return (USB_ERR_INVAL); req.bmRequestType = UT_WRITE_INTERFACE; req.bRequest = UR_SET_INTERFACE; req.wValue[0] = alt_no; req.wValue[1] = 0; req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_get_device_status * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_device_status(struct usb_device *udev, struct mtx *mtx, struct usb_status *st) { struct usb_device_request req; req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_STATUS; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, sizeof(*st)); return (usbd_do_request(udev, mtx, &req, st)); } /*------------------------------------------------------------------------* * usbd_req_get_hub_descriptor * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_hub_descriptor(struct usb_device *udev, struct mtx *mtx, struct usb_hub_descriptor *hd, uint8_t nports) { struct usb_device_request req; uint16_t len = (nports + 7 + (8 * 8)) / 8; req.bmRequestType = UT_READ_CLASS_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, UDESC_HUB, 0); USETW(req.wIndex, 0); USETW(req.wLength, len); return (usbd_do_request(udev, mtx, &req, hd)); } /*------------------------------------------------------------------------* * usbd_req_get_ss_hub_descriptor * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_ss_hub_descriptor(struct usb_device *udev, struct mtx *mtx, struct usb_hub_ss_descriptor *hd, uint8_t nports) { struct usb_device_request req; uint16_t len = sizeof(*hd) - 32 + 1 + ((nports + 7) / 8); req.bmRequestType = UT_READ_CLASS_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, UDESC_SS_HUB, 0); USETW(req.wIndex, 0); USETW(req.wLength, len); return (usbd_do_request(udev, mtx, &req, hd)); } /*------------------------------------------------------------------------* * usbd_req_get_hub_status * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_hub_status(struct usb_device *udev, struct mtx *mtx, struct usb_hub_status *st) { struct usb_device_request req; req.bmRequestType = UT_READ_CLASS_DEVICE; req.bRequest = UR_GET_STATUS; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, sizeof(struct usb_hub_status)); return (usbd_do_request(udev, mtx, &req, st)); } /*------------------------------------------------------------------------* * usbd_req_set_address * * This function is used to set the address for an USB device. After * port reset the USB device will respond at address zero. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr) { struct usb_device_request req; usb_error_t err; DPRINTFN(6, "setting device address=%d\n", addr); req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_SET_ADDRESS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, 0); err = USB_ERR_INVAL; /* check if USB controller handles set address */ if (udev->bus->methods->set_address != NULL) err = (udev->bus->methods->set_address) (udev, mtx, addr); if (err != USB_ERR_INVAL) goto done; /* Setting the address should not take more than 1 second ! */ err = usbd_do_request_flags(udev, mtx, &req, NULL, USB_DELAY_STATUS_STAGE, NULL, 1000); done: /* allow device time to set new address */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_set_address_settle)); return (err); } /*------------------------------------------------------------------------* * usbd_req_get_port_status * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_port_status(struct usb_device *udev, struct mtx *mtx, struct usb_port_status *ps, uint8_t port) { struct usb_device_request req; req.bmRequestType = UT_READ_CLASS_OTHER; req.bRequest = UR_GET_STATUS; USETW(req.wValue, 0); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, sizeof *ps); return (usbd_do_request(udev, mtx, &req, ps)); } /*------------------------------------------------------------------------* * usbd_req_clear_hub_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_clear_hub_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_DEVICE; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, sel); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_hub_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_DEVICE; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, sel); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_hub_u1_timeout * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_hub_u1_timeout(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint8_t timeout) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, UHF_PORT_U1_TIMEOUT); req.wIndex[0] = port; req.wIndex[1] = timeout; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_hub_u2_timeout * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_hub_u2_timeout(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint8_t timeout) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, UHF_PORT_U2_TIMEOUT); req.wIndex[0] = port; req.wIndex[1] = timeout; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_hub_depth * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_hub_depth(struct usb_device *udev, struct mtx *mtx, uint16_t depth) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_DEVICE; req.bRequest = UR_SET_HUB_DEPTH; USETW(req.wValue, depth); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_clear_port_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_clear_port_feature(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, sel); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_port_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_port_feature(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, sel); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_protocol * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_protocol(struct usb_device *udev, struct mtx *mtx, uint8_t iface_index, uint16_t report) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } DPRINTFN(5, "iface=%p, report=%d, endpt=%d\n", iface, report, iface->idesc->bInterfaceNumber); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_PROTOCOL; USETW(req.wValue, report); req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_report * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_report(struct usb_device *udev, struct mtx *mtx, void *data, uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } DPRINTFN(5, "len=%d\n", len); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_REPORT; USETW2(req.wValue, type, id); req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, len); return (usbd_do_request(udev, mtx, &req, data)); } /*------------------------------------------------------------------------* * usbd_req_get_report * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_report(struct usb_device *udev, struct mtx *mtx, void *data, uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } DPRINTFN(5, "len=%d\n", len); req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_REPORT; USETW2(req.wValue, type, id); req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, len); return (usbd_do_request(udev, mtx, &req, data)); } /*------------------------------------------------------------------------* * usbd_req_set_idle * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_idle(struct usb_device *udev, struct mtx *mtx, uint8_t iface_index, uint8_t duration, uint8_t id) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } DPRINTFN(5, "%d %d\n", duration, id); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_IDLE; USETW2(req.wValue, duration, id); req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_get_report_descriptor * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_report_descriptor(struct usb_device *udev, struct mtx *mtx, void *d, uint16_t size, uint8_t iface_index) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } req.bmRequestType = UT_READ_INTERFACE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */ req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, size); return (usbd_do_request(udev, mtx, &req, d)); } /*------------------------------------------------------------------------* * usbd_req_set_config * * This function is used to select the current configuration number in * both USB device side mode and USB host side mode. When setting the * configuration the function of the interfaces can change. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_config(struct usb_device *udev, struct mtx *mtx, uint8_t conf) { struct usb_device_request req; DPRINTF("setting config %d\n", conf); /* do "set configuration" request */ req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_SET_CONFIG; req.wValue[0] = conf; req.wValue[1] = 0; USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_get_config * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_config(struct usb_device *udev, struct mtx *mtx, uint8_t *pconf) { struct usb_device_request req; req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_CONFIG; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, 1); return (usbd_do_request(udev, mtx, &req, pconf)); } /*------------------------------------------------------------------------* * usbd_setup_device_desc *------------------------------------------------------------------------*/ usb_error_t usbd_setup_device_desc(struct usb_device *udev, struct mtx *mtx) { usb_error_t err; /* * Get the first 8 bytes of the device descriptor ! * * NOTE: "usbd_do_request()" will check the device descriptor * next time we do a request to see if the maximum packet size * changed! The 8 first bytes of the device descriptor * contains the maximum packet size to use on control endpoint * 0. If this value is different from "USB_MAX_IPACKET" a new * USB control request will be setup! */ switch (udev->speed) { case USB_SPEED_FULL: if (usb_full_ddesc != 0) { /* get full device descriptor */ err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); if (err == 0) break; } /* get partial device descriptor, some devices crash on this */ err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc, USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0); if (err != 0) break; /* get the full device descriptor */ err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); break; default: DPRINTF("Minimum bMaxPacketSize is large enough " "to hold the complete device descriptor or " "only one bMaxPacketSize choice\n"); /* get the full device descriptor */ err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); /* try one more time, if error */ if (err != 0) err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); break; } if (err != 0) { DPRINTFN(0, "getting device descriptor " "at addr %d failed, %s\n", udev->address, usbd_errstr(err)); return (err); } DPRINTF("adding unit addr=%d, rev=%02x, class=%d, " "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n", udev->address, UGETW(udev->ddesc.bcdUSB), udev->ddesc.bDeviceClass, udev->ddesc.bDeviceSubClass, udev->ddesc.bDeviceProtocol, udev->ddesc.bMaxPacketSize, udev->ddesc.bLength, udev->speed); return (err); } /*------------------------------------------------------------------------* * usbd_req_re_enumerate * * NOTE: After this function returns the hardware is in the * unconfigured state! The application is responsible for setting a * new configuration. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx) { struct usb_device *parent_hub; usb_error_t err; uint8_t old_addr; uint8_t do_retry = 1; if (udev->flags.usb_mode != USB_MODE_HOST) { return (USB_ERR_INVAL); } old_addr = udev->address; parent_hub = udev->parent_hub; if (parent_hub == NULL) { return (USB_ERR_INVAL); } retry: #if USB_HAVE_TT_SUPPORT /* * Try to reset the High Speed parent HUB of a LOW- or FULL- * speed device, if any. */ if (udev->parent_hs_hub != NULL && udev->speed != USB_SPEED_HIGH) { DPRINTF("Trying to reset parent High Speed TT.\n"); if (udev->parent_hs_hub == parent_hub && (uhub_count_active_host_ports(parent_hub, USB_SPEED_LOW) + uhub_count_active_host_ports(parent_hub, USB_SPEED_FULL)) == 1) { /* we can reset the whole TT */ err = usbd_req_reset_tt(parent_hub, NULL, udev->hs_port_no); } else { /* only reset a particular device and endpoint */ err = usbd_req_clear_tt_buffer(udev->parent_hs_hub, NULL, udev->hs_port_no, old_addr, UE_CONTROL, 0); } if (err) { DPRINTF("Resetting parent High " "Speed TT failed (%s).\n", usbd_errstr(err)); } } #endif /* Try to warm reset first */ if (parent_hub->speed == USB_SPEED_SUPER) usbd_req_warm_reset_port(parent_hub, mtx, udev->port_no); /* Try to reset the parent HUB port. */ err = usbd_req_reset_port(parent_hub, mtx, udev->port_no); if (err) { DPRINTFN(0, "addr=%d, port reset failed, %s\n", old_addr, usbd_errstr(err)); goto done; } /* * After that the port has been reset our device should be at * address zero: */ udev->address = USB_START_ADDR; /* reset "bMaxPacketSize" */ udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; /* reset USB state */ usb_set_device_state(udev, USB_STATE_POWERED); /* * Restore device address: */ err = usbd_req_set_address(udev, mtx, old_addr); if (err) { /* XXX ignore any errors! */ DPRINTFN(0, "addr=%d, set address failed! (%s, ignored)\n", old_addr, usbd_errstr(err)); } /* * Restore device address, if the controller driver did not * set a new one: */ if (udev->address == USB_START_ADDR) udev->address = old_addr; /* setup the device descriptor and the initial "wMaxPacketSize" */ err = usbd_setup_device_desc(udev, mtx); done: if (err && do_retry) { /* give the USB firmware some time to load */ usb_pause_mtx(mtx, hz / 2); /* no more retries after this retry */ do_retry = 0; /* try again */ goto retry; } /* restore address */ if (udev->address == USB_START_ADDR) udev->address = old_addr; /* update state, if successful */ if (err == 0) usb_set_device_state(udev, USB_STATE_ADDRESSED); return (err); } /*------------------------------------------------------------------------* * usbd_req_clear_device_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_clear_device_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, sel); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_device_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, sel); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_reset_tt * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_reset_tt(struct usb_device *udev, struct mtx *mtx, uint8_t port) { struct usb_device_request req; /* For single TT HUBs the port should be 1 */ if (udev->ddesc.bDeviceClass == UDCLASS_HUB && udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT) port = 1; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_RESET_TT; USETW(req.wValue, 0); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_clear_tt_buffer * * For single TT HUBs the port should be 1. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_clear_tt_buffer(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint8_t addr, uint8_t type, uint8_t endpoint) { struct usb_device_request req; uint16_t wValue; /* For single TT HUBs the port should be 1 */ if (udev->ddesc.bDeviceClass == UDCLASS_HUB && udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT) port = 1; wValue = (endpoint & 0xF) | ((addr & 0x7F) << 4) | ((endpoint & 0x80) << 8) | ((type & 3) << 12); req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_CLEAR_TT_BUFFER; USETW(req.wValue, wValue); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_port_link_state * * USB 3.0 specific request * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_port_link_state(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint8_t link_state) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, UHF_PORT_LINK_STATE); req.wIndex[0] = port; req.wIndex[1] = link_state; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_lpm_info * * USB 2.0 specific request for Link Power Management. * * Returns: * 0: Success * USB_ERR_PENDING_REQUESTS: NYET * USB_ERR_TIMEOUT: TIMEOUT * USB_ERR_STALL: STALL * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_lpm_info(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint8_t besl, uint8_t addr, uint8_t rwe) { struct usb_device_request req; usb_error_t err; uint8_t buf[1]; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_AND_TEST; USETW(req.wValue, UHF_PORT_L1); req.wIndex[0] = (port & 0xF) | ((besl & 0xF) << 4); req.wIndex[1] = (addr & 0x7F) | (rwe ? 0x80 : 0x00); USETW(req.wLength, sizeof(buf)); /* set default value in case of short transfer */ buf[0] = 0x00; err = usbd_do_request(udev, mtx, &req, buf); if (err) return (err); switch (buf[0]) { case 0x00: /* SUCCESS */ break; case 0x10: /* NYET */ err = USB_ERR_PENDING_REQUESTS; break; case 0x11: /* TIMEOUT */ err = USB_ERR_TIMEOUT; break; case 0x30: /* STALL */ err = USB_ERR_STALLED; break; default: /* reserved */ err = USB_ERR_IOERROR; break; } return (err); } Index: projects/clang390-import/sys/dev/usb/usb_transfer.c =================================================================== --- projects/clang390-import/sys/dev/usb/usb_transfer.c (revision 305430) +++ projects/clang390-import/sys/dev/usb/usb_transfer.c (revision 305431) @@ -1,3538 +1,3538 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ struct usb_std_packet_size { struct { uint16_t min; /* inclusive */ uint16_t max; /* inclusive */ } range; uint16_t fixed[4]; }; static usb_callback_t usb_request_callback; static const struct usb_config usb_control_ep_cfg[USB_CTRL_XFER_MAX] = { /* This transfer is used for generic control endpoint transfers */ [0] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control endpoint */ .direction = UE_DIR_ANY, .bufsize = USB_EP0_BUFSIZE, /* bytes */ .flags = {.proxy_buffer = 1,}, .callback = &usb_request_callback, .usb_mode = USB_MODE_DUAL, /* both modes */ }, /* This transfer is used for generic clear stall only */ [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &usb_do_clear_stall_callback, .timeout = 1000, /* 1 second */ .interval = 50, /* 50ms */ .usb_mode = USB_MODE_HOST, }, }; /* function prototypes */ static void usbd_update_max_frame_size(struct usb_xfer *); static void usbd_transfer_unsetup_sub(struct usb_xfer_root *, uint8_t); static void usbd_control_transfer_init(struct usb_xfer *); static int usbd_setup_ctrl_transfer(struct usb_xfer *); static void usb_callback_proc(struct usb_proc_msg *); static void usbd_callback_ss_done_defer(struct usb_xfer *); static void usbd_callback_wrapper(struct usb_xfer_queue *); static void usbd_transfer_start_cb(void *); static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *); static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr, uint8_t type, enum usb_dev_speed speed); /*------------------------------------------------------------------------* * usb_request_callback *------------------------------------------------------------------------*/ static void usb_request_callback(struct usb_xfer *xfer, usb_error_t error) { if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) usb_handle_request_callback(xfer, error); else usbd_do_request_callback(xfer, error); } /*------------------------------------------------------------------------* * usbd_update_max_frame_size * * This function updates the maximum frame size, hence high speed USB * can transfer multiple consecutive packets. *------------------------------------------------------------------------*/ static void usbd_update_max_frame_size(struct usb_xfer *xfer) { /* compute maximum frame size */ /* this computation should not overflow 16-bit */ /* max = 15 * 1024 */ xfer->max_frame_size = xfer->max_packet_size * xfer->max_packet_count; } /*------------------------------------------------------------------------* * usbd_get_dma_delay * * The following function is called when we need to * synchronize with DMA hardware. * * Returns: * 0: no DMA delay required * Else: milliseconds of DMA delay *------------------------------------------------------------------------*/ usb_timeout_t usbd_get_dma_delay(struct usb_device *udev) { const struct usb_bus_methods *mtod; uint32_t temp; mtod = udev->bus->methods; temp = 0; if (mtod->get_dma_delay) { (mtod->get_dma_delay) (udev, &temp); /* * Round up and convert to milliseconds. Note that we use * 1024 milliseconds per second. to save a division. */ temp += 0x3FF; temp /= 0x400; } return (temp); } /*------------------------------------------------------------------------* * usbd_transfer_setup_sub_malloc * * This function will allocate one or more DMA'able memory chunks * according to "size", "align" and "count" arguments. "ppc" is * pointed to a linear array of USB page caches afterwards. * * If the "align" argument is equal to "1" a non-contiguous allocation * can happen. Else if the "align" argument is greater than "1", the * allocation will always be contiguous in memory. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ #if USB_HAVE_BUSDMA uint8_t usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm, struct usb_page_cache **ppc, usb_size_t size, usb_size_t align, usb_size_t count) { struct usb_page_cache *pc; struct usb_page *pg; void *buf; usb_size_t n_dma_pc; usb_size_t n_dma_pg; usb_size_t n_obj; usb_size_t x; usb_size_t y; usb_size_t r; usb_size_t z; USB_ASSERT(align > 0, ("Invalid alignment, 0x%08x\n", align)); USB_ASSERT(size > 0, ("Invalid size = 0\n")); if (count == 0) { return (0); /* nothing to allocate */ } /* * Make sure that the size is aligned properly. */ size = -((-size) & (-align)); /* * Try multi-allocation chunks to reduce the number of DMA * allocations, hence DMA allocations are slow. */ if (align == 1) { /* special case - non-cached multi page DMA memory */ n_dma_pc = count; n_dma_pg = (2 + (size / USB_PAGE_SIZE)); n_obj = 1; } else if (size >= USB_PAGE_SIZE) { n_dma_pc = count; n_dma_pg = 1; n_obj = 1; } else { /* compute number of objects per page */ #ifdef USB_DMA_SINGLE_ALLOC n_obj = 1; #else n_obj = (USB_PAGE_SIZE / size); #endif /* * Compute number of DMA chunks, rounded up * to nearest one: */ n_dma_pc = howmany(count, n_obj); n_dma_pg = 1; } /* * DMA memory is allocated once, but mapped twice. That's why * there is one list for auto-free and another list for * non-auto-free which only holds the mapping and not the * allocation. */ if (parm->buf == NULL) { /* reserve memory (auto-free) */ parm->dma_page_ptr += n_dma_pc * n_dma_pg; parm->dma_page_cache_ptr += n_dma_pc; /* reserve memory (no-auto-free) */ parm->dma_page_ptr += count * n_dma_pg; parm->xfer_page_cache_ptr += count; return (0); } for (x = 0; x != n_dma_pc; x++) { /* need to initialize the page cache */ parm->dma_page_cache_ptr[x].tag_parent = &parm->curr_xfer->xroot->dma_parent_tag; } for (x = 0; x != count; x++) { /* need to initialize the page cache */ parm->xfer_page_cache_ptr[x].tag_parent = &parm->curr_xfer->xroot->dma_parent_tag; } if (ppc != NULL) { if (n_obj != 1) *ppc = parm->xfer_page_cache_ptr; else *ppc = parm->dma_page_cache_ptr; } r = count; /* set remainder count */ z = n_obj * size; /* set allocation size */ pc = parm->xfer_page_cache_ptr; pg = parm->dma_page_ptr; if (n_obj == 1) { /* * Avoid mapping memory twice if only a single object * should be allocated per page cache: */ for (x = 0; x != n_dma_pc; x++) { if (usb_pc_alloc_mem(parm->dma_page_cache_ptr, pg, z, align)) { return (1); /* failure */ } /* Make room for one DMA page cache and "n_dma_pg" pages */ parm->dma_page_cache_ptr++; pg += n_dma_pg; } } else { for (x = 0; x != n_dma_pc; x++) { if (r < n_obj) { /* compute last remainder */ z = r * size; n_obj = r; } if (usb_pc_alloc_mem(parm->dma_page_cache_ptr, pg, z, align)) { return (1); /* failure */ } /* Set beginning of current buffer */ buf = parm->dma_page_cache_ptr->buffer; /* Make room for one DMA page cache and "n_dma_pg" pages */ parm->dma_page_cache_ptr++; pg += n_dma_pg; for (y = 0; (y != n_obj); y++, r--, pc++, pg += n_dma_pg) { /* Load sub-chunk into DMA */ if (usb_pc_dmamap_create(pc, size)) { return (1); /* failure */ } pc->buffer = USB_ADD_BYTES(buf, y * size); pc->page_start = pg; mtx_lock(pc->tag_parent->mtx); if (usb_pc_load_mem(pc, size, 1 /* synchronous */ )) { mtx_unlock(pc->tag_parent->mtx); return (1); /* failure */ } mtx_unlock(pc->tag_parent->mtx); } } } parm->xfer_page_cache_ptr = pc; parm->dma_page_ptr = pg; return (0); } #endif /*------------------------------------------------------------------------* * usbd_transfer_setup_sub - transfer setup subroutine * * This function must be called from the "xfer_setup" callback of the * USB Host or Device controller driver when setting up an USB * transfer. This function will setup correct packet sizes, buffer * sizes, flags and more, that are stored in the "usb_xfer" * structure. *------------------------------------------------------------------------*/ void usbd_transfer_setup_sub(struct usb_setup_params *parm) { enum { REQ_SIZE = 8, MIN_PKT = 8, }; struct usb_xfer *xfer = parm->curr_xfer; const struct usb_config *setup = parm->curr_setup; struct usb_endpoint_ss_comp_descriptor *ecomp; struct usb_endpoint_descriptor *edesc; struct usb_std_packet_size std_size; usb_frcount_t n_frlengths; usb_frcount_t n_frbuffers; usb_frcount_t x; uint16_t maxp_old; uint8_t type; uint8_t zmps; /* * Sanity check. The following parameters must be initialized before * calling this function. */ if ((parm->hc_max_packet_size == 0) || (parm->hc_max_packet_count == 0) || (parm->hc_max_frame_size == 0)) { parm->err = USB_ERR_INVAL; goto done; } edesc = xfer->endpoint->edesc; ecomp = xfer->endpoint->ecomp; type = (edesc->bmAttributes & UE_XFERTYPE); xfer->flags = setup->flags; xfer->nframes = setup->frames; xfer->timeout = setup->timeout; xfer->callback = setup->callback; xfer->interval = setup->interval; xfer->endpointno = edesc->bEndpointAddress; xfer->max_packet_size = UGETW(edesc->wMaxPacketSize); xfer->max_packet_count = 1; /* make a shadow copy: */ xfer->flags_int.usb_mode = parm->udev->flags.usb_mode; parm->bufsize = setup->bufsize; switch (parm->speed) { case USB_SPEED_HIGH: switch (type) { case UE_ISOCHRONOUS: case UE_INTERRUPT: xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; /* check for invalid max packet count */ if (xfer->max_packet_count > 3) xfer->max_packet_count = 3; break; default: break; } xfer->max_packet_size &= 0x7FF; break; case USB_SPEED_SUPER: xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; if (ecomp != NULL) xfer->max_packet_count += ecomp->bMaxBurst; if ((xfer->max_packet_count == 0) || (xfer->max_packet_count > 16)) xfer->max_packet_count = 16; switch (type) { case UE_CONTROL: xfer->max_packet_count = 1; break; case UE_ISOCHRONOUS: if (ecomp != NULL) { uint8_t mult; mult = UE_GET_SS_ISO_MULT( ecomp->bmAttributes) + 1; if (mult > 3) mult = 3; xfer->max_packet_count *= mult; } break; default: break; } xfer->max_packet_size &= 0x7FF; break; default: break; } /* range check "max_packet_count" */ if (xfer->max_packet_count > parm->hc_max_packet_count) { xfer->max_packet_count = parm->hc_max_packet_count; } /* store max packet size value before filtering */ maxp_old = xfer->max_packet_size; /* filter "wMaxPacketSize" according to HC capabilities */ if ((xfer->max_packet_size > parm->hc_max_packet_size) || (xfer->max_packet_size == 0)) { xfer->max_packet_size = parm->hc_max_packet_size; } /* filter "wMaxPacketSize" according to standard sizes */ usbd_get_std_packet_size(&std_size, type, parm->speed); if (std_size.range.min || std_size.range.max) { if (xfer->max_packet_size < std_size.range.min) { xfer->max_packet_size = std_size.range.min; } if (xfer->max_packet_size > std_size.range.max) { xfer->max_packet_size = std_size.range.max; } } else { if (xfer->max_packet_size >= std_size.fixed[3]) { xfer->max_packet_size = std_size.fixed[3]; } else if (xfer->max_packet_size >= std_size.fixed[2]) { xfer->max_packet_size = std_size.fixed[2]; } else if (xfer->max_packet_size >= std_size.fixed[1]) { xfer->max_packet_size = std_size.fixed[1]; } else { /* only one possibility left */ xfer->max_packet_size = std_size.fixed[0]; } } /* * Check if the max packet size was outside its allowed range * and clamped to a valid value: */ if (maxp_old != xfer->max_packet_size) xfer->flags_int.maxp_was_clamped = 1; /* compute "max_frame_size" */ usbd_update_max_frame_size(xfer); /* check interrupt interval and transfer pre-delay */ if (type == UE_ISOCHRONOUS) { uint16_t frame_limit; xfer->interval = 0; /* not used, must be zero */ xfer->flags_int.isochronous_xfr = 1; /* set flag */ if (xfer->timeout == 0) { /* * set a default timeout in * case something goes wrong! */ xfer->timeout = 1000 / 4; } switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: frame_limit = USB_MAX_FS_ISOC_FRAMES_PER_XFER; xfer->fps_shift = 0; break; default: frame_limit = USB_MAX_HS_ISOC_FRAMES_PER_XFER; xfer->fps_shift = edesc->bInterval; if (xfer->fps_shift > 0) xfer->fps_shift--; if (xfer->fps_shift > 3) xfer->fps_shift = 3; if (xfer->flags.pre_scale_frames != 0) xfer->nframes <<= (3 - xfer->fps_shift); break; } if (xfer->nframes > frame_limit) { /* * this is not going to work * cross hardware */ parm->err = USB_ERR_INVAL; goto done; } if (xfer->nframes == 0) { /* * this is not a valid value */ parm->err = USB_ERR_ZERO_NFRAMES; goto done; } } else { /* * If a value is specified use that else check the * endpoint descriptor! */ if (type == UE_INTERRUPT) { uint32_t temp; if (xfer->interval == 0) { xfer->interval = edesc->bInterval; switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: break; default: /* 125us -> 1ms */ if (xfer->interval < 4) xfer->interval = 1; else if (xfer->interval > 16) xfer->interval = (1 << (16 - 4)); else xfer->interval = (1 << (xfer->interval - 4)); break; } } if (xfer->interval == 0) { /* * One millisecond is the smallest * interval we support: */ xfer->interval = 1; } xfer->fps_shift = 0; temp = 1; while ((temp != 0) && (temp < xfer->interval)) { xfer->fps_shift++; temp *= 2; } switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: break; default: xfer->fps_shift += 3; break; } } } /* * NOTE: we do not allow "max_packet_size" or "max_frame_size" * to be equal to zero when setting up USB transfers, hence * this leads to a lot of extra code in the USB kernel. */ if ((xfer->max_frame_size == 0) || (xfer->max_packet_size == 0)) { zmps = 1; if ((parm->bufsize <= MIN_PKT) && (type != UE_CONTROL) && (type != UE_BULK)) { /* workaround */ xfer->max_packet_size = MIN_PKT; xfer->max_packet_count = 1; parm->bufsize = 0; /* automatic setup length */ usbd_update_max_frame_size(xfer); } else { parm->err = USB_ERR_ZERO_MAXP; goto done; } } else { zmps = 0; } /* * check if we should setup a default * length: */ if (parm->bufsize == 0) { parm->bufsize = xfer->max_frame_size; if (type == UE_ISOCHRONOUS) { parm->bufsize *= xfer->nframes; } } /* * check if we are about to setup a proxy * type of buffer: */ if (xfer->flags.proxy_buffer) { /* round bufsize up */ parm->bufsize += (xfer->max_frame_size - 1); if (parm->bufsize < xfer->max_frame_size) { /* length wrapped around */ parm->err = USB_ERR_INVAL; goto done; } /* subtract remainder */ parm->bufsize -= (parm->bufsize % xfer->max_frame_size); /* add length of USB device request structure, if any */ if (type == UE_CONTROL) { parm->bufsize += REQ_SIZE; /* SETUP message */ } } xfer->max_data_length = parm->bufsize; /* Setup "n_frlengths" and "n_frbuffers" */ if (type == UE_ISOCHRONOUS) { n_frlengths = xfer->nframes; n_frbuffers = 1; } else { if (type == UE_CONTROL) { xfer->flags_int.control_xfr = 1; if (xfer->nframes == 0) { if (parm->bufsize <= REQ_SIZE) { /* * there will never be any data * stage */ xfer->nframes = 1; } else { xfer->nframes = 2; } } } else { if (xfer->nframes == 0) { xfer->nframes = 1; } } n_frlengths = xfer->nframes; n_frbuffers = xfer->nframes; } /* * check if we have room for the * USB device request structure: */ if (type == UE_CONTROL) { if (xfer->max_data_length < REQ_SIZE) { /* length wrapped around or too small bufsize */ parm->err = USB_ERR_INVAL; goto done; } xfer->max_data_length -= REQ_SIZE; } /* * Setup "frlengths" and shadow "frlengths" for keeping the * initial frame lengths when a USB transfer is complete. This * information is useful when computing isochronous offsets. */ xfer->frlengths = parm->xfer_length_ptr; parm->xfer_length_ptr += 2 * n_frlengths; /* setup "frbuffers" */ xfer->frbuffers = parm->xfer_page_cache_ptr; parm->xfer_page_cache_ptr += n_frbuffers; /* initialize max frame count */ xfer->max_frame_count = xfer->nframes; /* * check if we need to setup * a local buffer: */ if (!xfer->flags.ext_buffer) { #if USB_HAVE_BUSDMA struct usb_page_search page_info; struct usb_page_cache *pc; if (usbd_transfer_setup_sub_malloc(parm, &pc, parm->bufsize, 1, 1)) { parm->err = USB_ERR_NOMEM; } else if (parm->buf != NULL) { usbd_get_page(pc, 0, &page_info); xfer->local_buffer = page_info.buffer; usbd_xfer_set_frame_offset(xfer, 0, 0); if ((type == UE_CONTROL) && (n_frbuffers > 1)) { usbd_xfer_set_frame_offset(xfer, REQ_SIZE, 1); } } #else /* align data */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); if (parm->buf != NULL) { xfer->local_buffer = USB_ADD_BYTES(parm->buf, parm->size[0]); usbd_xfer_set_frame_offset(xfer, 0, 0); if ((type == UE_CONTROL) && (n_frbuffers > 1)) { usbd_xfer_set_frame_offset(xfer, REQ_SIZE, 1); } } parm->size[0] += parm->bufsize; /* align data again */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); #endif } /* * Compute maximum buffer size */ if (parm->bufsize_max < parm->bufsize) { parm->bufsize_max = parm->bufsize; } #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) { /* * Setup "dma_page_ptr". * * Proof for formula below: * * Assume there are three USB frames having length "a", "b" and * "c". These USB frames will at maximum need "z" * "usb_page" structures. "z" is given by: * * z = ((a / USB_PAGE_SIZE) + 2) + ((b / USB_PAGE_SIZE) + 2) + * ((c / USB_PAGE_SIZE) + 2); * * Constraining "a", "b" and "c" like this: * * (a + b + c) <= parm->bufsize * * We know that: * * z <= ((parm->bufsize / USB_PAGE_SIZE) + (3*2)); * * Here is the general formula: */ xfer->dma_page_ptr = parm->dma_page_ptr; parm->dma_page_ptr += (2 * n_frbuffers); parm->dma_page_ptr += (parm->bufsize / USB_PAGE_SIZE); } #endif if (zmps) { /* correct maximum data length */ xfer->max_data_length = 0; } /* subtract USB frame remainder from "hc_max_frame_size" */ xfer->max_hc_frame_size = (parm->hc_max_frame_size - (parm->hc_max_frame_size % xfer->max_frame_size)); if (xfer->max_hc_frame_size == 0) { parm->err = USB_ERR_INVAL; goto done; } /* initialize frame buffers */ if (parm->buf) { for (x = 0; x != n_frbuffers; x++) { xfer->frbuffers[x].tag_parent = &xfer->xroot->dma_parent_tag; #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable && (parm->bufsize_max > 0)) { if (usb_pc_dmamap_create( xfer->frbuffers + x, parm->bufsize_max)) { parm->err = USB_ERR_NOMEM; goto done; } } #endif } } done: if (parm->err) { /* * Set some dummy values so that we avoid division by zero: */ xfer->max_hc_frame_size = 1; xfer->max_frame_size = 1; xfer->max_packet_size = 1; xfer->max_data_length = 0; xfer->nframes = 0; xfer->max_frame_count = 0; } } static uint8_t usbd_transfer_setup_has_bulk(const struct usb_config *setup_start, uint16_t n_setup) { while (n_setup--) { uint8_t type = setup_start[n_setup].type; if (type == UE_BULK || type == UE_BULK_INTR || type == UE_TYPE_ANY) return (1); } return (0); } /*------------------------------------------------------------------------* * usbd_transfer_setup - setup an array of USB transfers * * NOTE: You must always call "usbd_transfer_unsetup" after calling * "usbd_transfer_setup" if success was returned. * * The idea is that the USB device driver should pre-allocate all its * transfers by one call to this function. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_transfer_setup(struct usb_device *udev, const uint8_t *ifaces, struct usb_xfer **ppxfer, const struct usb_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *xfer_mtx) { const struct usb_config *setup_end = setup_start + n_setup; const struct usb_config *setup; struct usb_setup_params *parm; struct usb_endpoint *ep; struct usb_xfer_root *info; struct usb_xfer *xfer; void *buf = NULL; usb_error_t error = 0; uint16_t n; uint16_t refcount; uint8_t do_unlock; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_setup can sleep!"); /* do some checking first */ if (n_setup == 0) { DPRINTFN(6, "setup array has zero length!\n"); return (USB_ERR_INVAL); } if (ifaces == NULL) { DPRINTFN(6, "ifaces array is NULL!\n"); return (USB_ERR_INVAL); } if (xfer_mtx == NULL) { DPRINTFN(6, "using global lock\n"); xfer_mtx = &Giant; } /* more sanity checks */ for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { if (setup->bufsize == (usb_frlength_t)-1) { error = USB_ERR_BAD_BUFSIZE; DPRINTF("invalid bufsize\n"); } if (setup->callback == NULL) { error = USB_ERR_NO_CALLBACK; DPRINTF("no callback\n"); } ppxfer[n] = NULL; } if (error) return (error); /* Protect scratch area */ - do_unlock = usbd_enum_lock(udev); + do_unlock = usbd_ctrl_lock(udev); refcount = 0; info = NULL; parm = &udev->scratch.xfer_setup[0].parm; memset(parm, 0, sizeof(*parm)); parm->udev = udev; parm->speed = usbd_get_speed(udev); parm->hc_max_packet_count = 1; if (parm->speed >= USB_SPEED_MAX) { parm->err = USB_ERR_INVAL; goto done; } /* setup all transfers */ while (1) { if (buf) { /* * Initialize the "usb_xfer_root" structure, * which is common for all our USB transfers. */ info = USB_ADD_BYTES(buf, 0); info->memory_base = buf; info->memory_size = parm->size[0]; #if USB_HAVE_BUSDMA info->dma_page_cache_start = USB_ADD_BYTES(buf, parm->size[4]); info->dma_page_cache_end = USB_ADD_BYTES(buf, parm->size[5]); #endif info->xfer_page_cache_start = USB_ADD_BYTES(buf, parm->size[5]); info->xfer_page_cache_end = USB_ADD_BYTES(buf, parm->size[2]); cv_init(&info->cv_drain, "WDRAIN"); info->xfer_mtx = xfer_mtx; #if USB_HAVE_BUSDMA usb_dma_tag_setup(&info->dma_parent_tag, parm->dma_tag_p, udev->bus->dma_parent_tag[0].tag, xfer_mtx, &usb_bdma_done_event, udev->bus->dma_bits, parm->dma_tag_max); #endif info->bus = udev->bus; info->udev = udev; TAILQ_INIT(&info->done_q.head); info->done_q.command = &usbd_callback_wrapper; #if USB_HAVE_BUSDMA TAILQ_INIT(&info->dma_q.head); info->dma_q.command = &usb_bdma_work_loop; #endif info->done_m[0].hdr.pm_callback = &usb_callback_proc; info->done_m[0].xroot = info; info->done_m[1].hdr.pm_callback = &usb_callback_proc; info->done_m[1].xroot = info; /* * In device side mode control endpoint * requests need to run from a separate * context, else there is a chance of * deadlock! */ if (setup_start == usb_control_ep_cfg) info->done_p = USB_BUS_CONTROL_XFER_PROC(udev->bus); else if (xfer_mtx == &Giant) info->done_p = USB_BUS_GIANT_PROC(udev->bus); else if (usbd_transfer_setup_has_bulk(setup_start, n_setup)) info->done_p = USB_BUS_NON_GIANT_BULK_PROC(udev->bus); else info->done_p = USB_BUS_NON_GIANT_ISOC_PROC(udev->bus); } /* reset sizes */ parm->size[0] = 0; parm->buf = buf; parm->size[0] += sizeof(info[0]); for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { /* skip USB transfers without callbacks: */ if (setup->callback == NULL) { continue; } /* see if there is a matching endpoint */ ep = usbd_get_endpoint(udev, ifaces[setup->if_index], setup); /* * Check that the USB PIPE is valid and that * the endpoint mode is proper. * * Make sure we don't allocate a streams * transfer when such a combination is not * valid. */ if ((ep == NULL) || (ep->methods == NULL) || ((ep->ep_mode != USB_EP_MODE_STREAMS) && (ep->ep_mode != USB_EP_MODE_DEFAULT)) || (setup->stream_id != 0 && (setup->stream_id >= USB_MAX_EP_STREAMS || (ep->ep_mode != USB_EP_MODE_STREAMS)))) { if (setup->flags.no_pipe_ok) continue; if ((setup->usb_mode != USB_MODE_DUAL) && (setup->usb_mode != udev->flags.usb_mode)) continue; parm->err = USB_ERR_NO_PIPE; goto done; } /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store current setup pointer */ parm->curr_setup = setup; if (buf) { /* * Common initialization of the * "usb_xfer" structure. */ xfer = USB_ADD_BYTES(buf, parm->size[0]); xfer->address = udev->address; xfer->priv_sc = priv_sc; xfer->xroot = info; usb_callout_init_mtx(&xfer->timeout_handle, &udev->bus->bus_mtx, 0); } else { /* * Setup a dummy xfer, hence we are * writing to the "usb_xfer" * structure pointed to by "xfer" * before we have allocated any * memory: */ xfer = &udev->scratch.xfer_setup[0].dummy; memset(xfer, 0, sizeof(*xfer)); refcount++; } /* set transfer endpoint pointer */ xfer->endpoint = ep; /* set transfer stream ID */ xfer->stream_id = setup->stream_id; parm->size[0] += sizeof(xfer[0]); parm->methods = xfer->endpoint->methods; parm->curr_xfer = xfer; /* * Call the Host or Device controller transfer * setup routine: */ (udev->bus->methods->xfer_setup) (parm); /* check for error */ if (parm->err) goto done; if (buf) { /* * Increment the endpoint refcount. This * basically prevents setting a new * configuration and alternate setting * when USB transfers are in use on * the given interface. Search the USB * code for "endpoint->refcount_alloc" if you * want more information. */ USB_BUS_LOCK(info->bus); if (xfer->endpoint->refcount_alloc >= USB_EP_REF_MAX) parm->err = USB_ERR_INVAL; xfer->endpoint->refcount_alloc++; if (xfer->endpoint->refcount_alloc == 0) panic("usbd_transfer_setup(): Refcount wrapped to zero\n"); USB_BUS_UNLOCK(info->bus); /* * Whenever we set ppxfer[] then we * also need to increment the * "setup_refcount": */ info->setup_refcount++; /* * Transfer is successfully setup and * can be used: */ ppxfer[n] = xfer; } /* check for error */ if (parm->err) goto done; } if (buf != NULL || parm->err != 0) goto done; /* if no transfers, nothing to do */ if (refcount == 0) goto done; /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[1] = parm->size[0]; /* * The number of DMA tags required depends on * the number of endpoints. The current estimate * for maximum number of DMA tags per endpoint * is three: * 1) for loading memory * 2) for allocating memory * 3) for fixing memory [UHCI] */ parm->dma_tag_max += 3 * MIN(n_setup, USB_EP_MAX); /* * DMA tags for QH, TD, Data and more. */ parm->dma_tag_max += 8; parm->dma_tag_p += parm->dma_tag_max; parm->size[0] += ((uint8_t *)parm->dma_tag_p) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[3] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->dma_page_ptr) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[4] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->dma_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm->size[5] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->xfer_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm->size[2] = parm->size[0]; /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); parm->size[6] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->xfer_length_ptr) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* allocate zeroed memory */ buf = malloc(parm->size[0], M_USB, M_WAITOK | M_ZERO); if (buf == NULL) { parm->err = USB_ERR_NOMEM; DPRINTFN(0, "cannot allocate memory block for " "configuration (%d bytes)\n", parm->size[0]); goto done; } parm->dma_tag_p = USB_ADD_BYTES(buf, parm->size[1]); parm->dma_page_ptr = USB_ADD_BYTES(buf, parm->size[3]); parm->dma_page_cache_ptr = USB_ADD_BYTES(buf, parm->size[4]); parm->xfer_page_cache_ptr = USB_ADD_BYTES(buf, parm->size[5]); parm->xfer_length_ptr = USB_ADD_BYTES(buf, parm->size[6]); } done: if (buf) { if (info->setup_refcount == 0) { /* * "usbd_transfer_unsetup_sub" will unlock * the bus mutex before returning ! */ USB_BUS_LOCK(info->bus); /* something went wrong */ usbd_transfer_unsetup_sub(info, 0); } } /* check if any errors happened */ if (parm->err) usbd_transfer_unsetup(ppxfer, n_setup); error = parm->err; if (do_unlock) - usbd_enum_unlock(udev); + usbd_ctrl_unlock(udev); return (error); } /*------------------------------------------------------------------------* * usbd_transfer_unsetup_sub - factored out code *------------------------------------------------------------------------*/ static void usbd_transfer_unsetup_sub(struct usb_xfer_root *info, uint8_t needs_delay) { #if USB_HAVE_BUSDMA struct usb_page_cache *pc; #endif USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); /* wait for any outstanding DMA operations */ if (needs_delay) { usb_timeout_t temp; temp = usbd_get_dma_delay(info->udev); if (temp != 0) { usb_pause_mtx(&info->bus->bus_mtx, USB_MS_TO_TICKS(temp)); } } /* make sure that our done messages are not queued anywhere */ usb_proc_mwait(info->done_p, &info->done_m[0], &info->done_m[1]); USB_BUS_UNLOCK(info->bus); #if USB_HAVE_BUSDMA /* free DMA'able memory, if any */ pc = info->dma_page_cache_start; while (pc != info->dma_page_cache_end) { usb_pc_free_mem(pc); pc++; } /* free DMA maps in all "xfer->frbuffers" */ pc = info->xfer_page_cache_start; while (pc != info->xfer_page_cache_end) { usb_pc_dmamap_destroy(pc); pc++; } /* free all DMA tags */ usb_dma_tag_unsetup(&info->dma_parent_tag); #endif cv_destroy(&info->cv_drain); /* * free the "memory_base" last, hence the "info" structure is * contained within the "memory_base"! */ free(info->memory_base, M_USB); } /*------------------------------------------------------------------------* * usbd_transfer_unsetup - unsetup/free an array of USB transfers * * NOTE: All USB transfers in progress will get called back passing * the error code "USB_ERR_CANCELLED" before this function * returns. *------------------------------------------------------------------------*/ void usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup) { struct usb_xfer *xfer; struct usb_xfer_root *info; uint8_t needs_delay = 0; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_unsetup can sleep!"); while (n_setup--) { xfer = pxfer[n_setup]; if (xfer == NULL) continue; info = xfer->xroot; USB_XFER_LOCK(xfer); USB_BUS_LOCK(info->bus); /* * HINT: when you start/stop a transfer, it might be a * good idea to directly use the "pxfer[]" structure: * * usbd_transfer_start(sc->pxfer[0]); * usbd_transfer_stop(sc->pxfer[0]); * * That way, if your code has many parts that will not * stop running under the same lock, in other words * "xfer_mtx", the usbd_transfer_start and * usbd_transfer_stop functions will simply return * when they detect a NULL pointer argument. * * To avoid any races we clear the "pxfer[]" pointer * while holding the private mutex of the driver: */ pxfer[n_setup] = NULL; USB_BUS_UNLOCK(info->bus); USB_XFER_UNLOCK(xfer); usbd_transfer_drain(xfer); #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) needs_delay = 1; #endif /* * NOTE: default endpoint does not have an * interface, even if endpoint->iface_index == 0 */ USB_BUS_LOCK(info->bus); xfer->endpoint->refcount_alloc--; USB_BUS_UNLOCK(info->bus); usb_callout_drain(&xfer->timeout_handle); USB_BUS_LOCK(info->bus); USB_ASSERT(info->setup_refcount != 0, ("Invalid setup " "reference count\n")); info->setup_refcount--; if (info->setup_refcount == 0) { usbd_transfer_unsetup_sub(info, needs_delay); } else { USB_BUS_UNLOCK(info->bus); } } } /*------------------------------------------------------------------------* * usbd_control_transfer_init - factored out code * * In USB Device Mode we have to wait for the SETUP packet which * containst the "struct usb_device_request" structure, before we can * transfer any data. In USB Host Mode we already have the SETUP * packet at the moment the USB transfer is started. This leads us to * having to setup the USB transfer at two different places in * time. This function just contains factored out control transfer * initialisation code, so that we don't duplicate the code. *------------------------------------------------------------------------*/ static void usbd_control_transfer_init(struct usb_xfer *xfer) { struct usb_device_request req; /* copy out the USB request header */ usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); /* setup remainder */ xfer->flags_int.control_rem = UGETW(req.wLength); /* copy direction to endpoint variable */ xfer->endpointno &= ~(UE_DIR_IN | UE_DIR_OUT); xfer->endpointno |= (req.bmRequestType & UT_READ) ? UE_DIR_IN : UE_DIR_OUT; } /*------------------------------------------------------------------------* * usbd_control_transfer_did_data * * This function returns non-zero if a control endpoint has * transferred the first DATA packet after the SETUP packet. * Else it returns zero. *------------------------------------------------------------------------*/ static uint8_t usbd_control_transfer_did_data(struct usb_xfer *xfer) { struct usb_device_request req; /* SETUP packet is not yet sent */ if (xfer->flags_int.control_hdr != 0) return (0); /* copy out the USB request header */ usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); /* compare remainder to the initial value */ return (xfer->flags_int.control_rem != UGETW(req.wLength)); } /*------------------------------------------------------------------------* * usbd_setup_ctrl_transfer * * This function handles initialisation of control transfers. Control * transfers are special in that regard that they can both transmit * and receive data. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static int usbd_setup_ctrl_transfer(struct usb_xfer *xfer) { usb_frlength_t len; /* Check for control endpoint stall */ if (xfer->flags.stall_pipe && xfer->flags_int.control_act) { /* the control transfer is no longer active */ xfer->flags_int.control_stall = 1; xfer->flags_int.control_act = 0; } else { /* don't stall control transfer by default */ xfer->flags_int.control_stall = 0; } /* Check for invalid number of frames */ if (xfer->nframes > 2) { /* * If you need to split a control transfer, you * have to do one part at a time. Only with * non-control transfers you can do multiple * parts a time. */ DPRINTFN(0, "Too many frames: %u\n", (unsigned int)xfer->nframes); goto error; } /* * Check if there is a control * transfer in progress: */ if (xfer->flags_int.control_act) { if (xfer->flags_int.control_hdr) { /* clear send header flag */ xfer->flags_int.control_hdr = 0; /* setup control transfer */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { usbd_control_transfer_init(xfer); } } /* get data length */ len = xfer->sumlen; } else { /* the size of the SETUP structure is hardcoded ! */ if (xfer->frlengths[0] != sizeof(struct usb_device_request)) { DPRINTFN(0, "Wrong framelength %u != %zu\n", xfer->frlengths[0], sizeof(struct usb_device_request)); goto error; } /* check USB mode */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { /* check number of frames */ if (xfer->nframes != 1) { /* * We need to receive the setup * message first so that we know the * data direction! */ DPRINTF("Misconfigured transfer\n"); goto error; } /* * Set a dummy "control_rem" value. This * variable will be overwritten later by a * call to "usbd_control_transfer_init()" ! */ xfer->flags_int.control_rem = 0xFFFF; } else { /* setup "endpoint" and "control_rem" */ usbd_control_transfer_init(xfer); } /* set transfer-header flag */ xfer->flags_int.control_hdr = 1; /* get data length */ len = (xfer->sumlen - sizeof(struct usb_device_request)); } /* update did data flag */ xfer->flags_int.control_did_data = usbd_control_transfer_did_data(xfer); /* check if there is a length mismatch */ if (len > xfer->flags_int.control_rem) { DPRINTFN(0, "Length (%d) greater than " "remaining length (%d)\n", len, xfer->flags_int.control_rem); goto error; } /* check if we are doing a short transfer */ if (xfer->flags.force_short_xfer) { xfer->flags_int.control_rem = 0; } else { if ((len != xfer->max_data_length) && (len != xfer->flags_int.control_rem) && (xfer->nframes != 1)) { DPRINTFN(0, "Short control transfer without " "force_short_xfer set\n"); goto error; } xfer->flags_int.control_rem -= len; } /* the status part is executed when "control_act" is 0 */ if ((xfer->flags_int.control_rem > 0) || (xfer->flags.manual_status)) { /* don't execute the STATUS stage yet */ xfer->flags_int.control_act = 1; /* sanity check */ if ((!xfer->flags_int.control_hdr) && (xfer->nframes == 1)) { /* * This is not a valid operation! */ DPRINTFN(0, "Invalid parameter " "combination\n"); goto error; } } else { /* time to execute the STATUS stage */ xfer->flags_int.control_act = 0; } return (0); /* success */ error: return (1); /* failure */ } /*------------------------------------------------------------------------* * usbd_transfer_submit - start USB hardware for the given transfer * * This function should only be called from the USB callback. *------------------------------------------------------------------------*/ void usbd_transfer_submit(struct usb_xfer *xfer) { struct usb_xfer_root *info; struct usb_bus *bus; usb_frcount_t x; info = xfer->xroot; bus = info->bus; DPRINTF("xfer=%p, endpoint=%p, nframes=%d, dir=%s\n", xfer, xfer->endpoint, xfer->nframes, USB_GET_DATA_ISREAD(xfer) ? "read" : "write"); #ifdef USB_DEBUG if (USB_DEBUG_VAR > 0) { USB_BUS_LOCK(bus); usb_dump_endpoint(xfer->endpoint); USB_BUS_UNLOCK(bus); } #endif USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK_ASSERT(bus, MA_NOTOWNED); /* Only open the USB transfer once! */ if (!xfer->flags_int.open) { xfer->flags_int.open = 1; DPRINTF("open\n"); USB_BUS_LOCK(bus); (xfer->endpoint->methods->open) (xfer); USB_BUS_UNLOCK(bus); } /* set "transferring" flag */ xfer->flags_int.transferring = 1; #if USB_HAVE_POWERD /* increment power reference */ usbd_transfer_power_ref(xfer, 1); #endif /* * Check if the transfer is waiting on a queue, most * frequently the "done_q": */ if (xfer->wait_queue) { USB_BUS_LOCK(bus); usbd_transfer_dequeue(xfer); USB_BUS_UNLOCK(bus); } /* clear "did_dma_delay" flag */ xfer->flags_int.did_dma_delay = 0; /* clear "did_close" flag */ xfer->flags_int.did_close = 0; #if USB_HAVE_BUSDMA /* clear "bdma_setup" flag */ xfer->flags_int.bdma_setup = 0; #endif /* by default we cannot cancel any USB transfer immediately */ xfer->flags_int.can_cancel_immed = 0; /* clear lengths and frame counts by default */ xfer->sumlen = 0; xfer->actlen = 0; xfer->aframes = 0; /* clear any previous errors */ xfer->error = 0; /* Check if the device is still alive */ if (info->udev->state < USB_STATE_POWERED) { USB_BUS_LOCK(bus); /* * Must return cancelled error code else * device drivers can hang. */ usbd_transfer_done(xfer, USB_ERR_CANCELLED); USB_BUS_UNLOCK(bus); return; } /* sanity check */ if (xfer->nframes == 0) { if (xfer->flags.stall_pipe) { /* * Special case - want to stall without transferring * any data: */ DPRINTF("xfer=%p nframes=0: stall " "or clear stall!\n", xfer); USB_BUS_LOCK(bus); xfer->flags_int.can_cancel_immed = 1; /* start the transfer */ usb_command_wrapper(&xfer->endpoint-> endpoint_q[xfer->stream_id], xfer); USB_BUS_UNLOCK(bus); return; } USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(bus); return; } /* compute some variables */ for (x = 0; x != xfer->nframes; x++) { /* make a copy of the frlenghts[] */ xfer->frlengths[x + xfer->max_frame_count] = xfer->frlengths[x]; /* compute total transfer length */ xfer->sumlen += xfer->frlengths[x]; if (xfer->sumlen < xfer->frlengths[x]) { /* length wrapped around */ USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(bus); return; } } /* clear some internal flags */ xfer->flags_int.short_xfer_ok = 0; xfer->flags_int.short_frames_ok = 0; /* check if this is a control transfer */ if (xfer->flags_int.control_xfr) { if (usbd_setup_ctrl_transfer(xfer)) { USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_STALLED); USB_BUS_UNLOCK(bus); return; } } /* * Setup filtered version of some transfer flags, * in case of data read direction */ if (USB_GET_DATA_ISREAD(xfer)) { if (xfer->flags.short_frames_ok) { xfer->flags_int.short_xfer_ok = 1; xfer->flags_int.short_frames_ok = 1; } else if (xfer->flags.short_xfer_ok) { xfer->flags_int.short_xfer_ok = 1; /* check for control transfer */ if (xfer->flags_int.control_xfr) { /* * 1) Control transfers do not support * reception of multiple short USB * frames in host mode and device side * mode, with exception of: * * 2) Due to sometimes buggy device * side firmware we need to do a * STATUS stage in case of short * control transfers in USB host mode. * The STATUS stage then becomes the * "alt_next" to the DATA stage. */ xfer->flags_int.short_frames_ok = 1; } } } /* * Check if BUS-DMA support is enabled and try to load virtual * buffers into DMA, if any: */ #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) { /* insert the USB transfer last in the BUS-DMA queue */ usb_command_wrapper(&xfer->xroot->dma_q, xfer); return; } #endif /* * Enter the USB transfer into the Host Controller or * Device Controller schedule: */ usbd_pipe_enter(xfer); } /*------------------------------------------------------------------------* * usbd_pipe_enter - factored out code *------------------------------------------------------------------------*/ void usbd_pipe_enter(struct usb_xfer *xfer) { struct usb_endpoint *ep; USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK(xfer->xroot->bus); ep = xfer->endpoint; DPRINTF("enter\n"); /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* enter the transfer */ (ep->methods->enter) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); USB_BUS_UNLOCK(xfer->xroot->bus); return; } /* start the transfer */ usb_command_wrapper(&ep->endpoint_q[xfer->stream_id], xfer); USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_start - start an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer start, until the USB transfer * completes. *------------------------------------------------------------------------*/ void usbd_transfer_start(struct usb_xfer *xfer) { if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* mark the USB transfer started */ if (!xfer->flags_int.started) { /* lock the BUS lock to avoid races updating flags_int */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags_int.started = 1; USB_BUS_UNLOCK(xfer->xroot->bus); } /* check if the USB transfer callback is already transferring */ if (xfer->flags_int.transferring) { return; } USB_BUS_LOCK(xfer->xroot->bus); /* call the USB transfer callback */ usbd_callback_ss_done_defer(xfer); USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_stop - stop an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer stop. * NOTE: When this function returns it is not safe to free nor * reuse any DMA buffers. See "usbd_transfer_drain()". *------------------------------------------------------------------------*/ void usbd_transfer_stop(struct usb_xfer *xfer) { struct usb_endpoint *ep; if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* check if the USB transfer was ever opened */ if (!xfer->flags_int.open) { if (xfer->flags_int.started) { /* nothing to do except clearing the "started" flag */ /* lock the BUS lock to avoid races updating flags_int */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags_int.started = 0; USB_BUS_UNLOCK(xfer->xroot->bus); } return; } /* try to stop the current USB transfer */ USB_BUS_LOCK(xfer->xroot->bus); /* override any previous error */ xfer->error = USB_ERR_CANCELLED; /* * Clear "open" and "started" when both private and USB lock * is locked so that we don't get a race updating "flags_int" */ xfer->flags_int.open = 0; xfer->flags_int.started = 0; /* * Check if we can cancel the USB transfer immediately. */ if (xfer->flags_int.transferring) { if (xfer->flags_int.can_cancel_immed && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); /* * The following will lead to an USB_ERR_CANCELLED * error code being passed to the USB callback. */ (xfer->endpoint->methods->close) (xfer); /* only close once */ xfer->flags_int.did_close = 1; } else { /* need to wait for the next done callback */ } } else { DPRINTF("close\n"); /* close here and now */ (xfer->endpoint->methods->close) (xfer); /* * Any additional DMA delay is done by * "usbd_transfer_unsetup()". */ /* * Special case. Check if we need to restart a blocked * endpoint. */ ep = xfer->endpoint; /* * If the current USB transfer is completing we need * to start the next one: */ if (ep->endpoint_q[xfer->stream_id].curr == xfer) { usb_command_wrapper( &ep->endpoint_q[xfer->stream_id], NULL); } } USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_pending * * This function will check if an USB transfer is pending which is a * little bit complicated! * Return values: * 0: Not pending * 1: Pending: The USB transfer will receive a callback in the future. *------------------------------------------------------------------------*/ uint8_t usbd_transfer_pending(struct usb_xfer *xfer) { struct usb_xfer_root *info; struct usb_xfer_queue *pq; if (xfer == NULL) { /* transfer is gone */ return (0); } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); if (xfer->flags_int.transferring) { /* trivial case */ return (1); } USB_BUS_LOCK(xfer->xroot->bus); if (xfer->wait_queue) { /* we are waiting on a queue somewhere */ USB_BUS_UNLOCK(xfer->xroot->bus); return (1); } info = xfer->xroot; pq = &info->done_q; if (pq->curr == xfer) { /* we are currently scheduled for callback */ USB_BUS_UNLOCK(xfer->xroot->bus); return (1); } /* we are not pending */ USB_BUS_UNLOCK(xfer->xroot->bus); return (0); } /*------------------------------------------------------------------------* * usbd_transfer_drain * * This function will stop the USB transfer and wait for any * additional BUS-DMA and HW-DMA operations to complete. Buffers that * are loaded into DMA can safely be freed or reused after that this * function has returned. *------------------------------------------------------------------------*/ void usbd_transfer_drain(struct usb_xfer *xfer) { WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_drain can sleep!"); if (xfer == NULL) { /* transfer is gone */ return; } if (xfer->xroot->xfer_mtx != &Giant) { USB_XFER_LOCK_ASSERT(xfer, MA_NOTOWNED); } USB_XFER_LOCK(xfer); usbd_transfer_stop(xfer); while (usbd_transfer_pending(xfer) || xfer->flags_int.doing_callback) { /* * It is allowed that the callback can drop its * transfer mutex. In that case checking only * "usbd_transfer_pending()" is not enough to tell if * the USB transfer is fully drained. We also need to * check the internal "doing_callback" flag. */ xfer->flags_int.draining = 1; /* * Wait until the current outstanding USB * transfer is complete ! */ cv_wait(&xfer->xroot->cv_drain, xfer->xroot->xfer_mtx); } USB_XFER_UNLOCK(xfer); } struct usb_page_cache * usbd_xfer_get_frame(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (&xfer->frbuffers[frindex]); } void * usbd_xfer_get_frame_buffer(struct usb_xfer *xfer, usb_frcount_t frindex) { struct usb_page_search page_info; KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); usbd_get_page(&xfer->frbuffers[frindex], 0, &page_info); return (page_info.buffer); } /*------------------------------------------------------------------------* * usbd_xfer_get_fps_shift * * The following function is only useful for isochronous transfers. It * returns how many times the frame execution rate has been shifted * down. * * Return value: * Success: 0..3 * Failure: 0 *------------------------------------------------------------------------*/ uint8_t usbd_xfer_get_fps_shift(struct usb_xfer *xfer) { return (xfer->fps_shift); } usb_frlength_t usbd_xfer_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (xfer->frlengths[frindex]); } /*------------------------------------------------------------------------* * usbd_xfer_set_frame_data * * This function sets the pointer of the buffer that should * loaded directly into DMA for the given USB frame. Passing "ptr" * equal to NULL while the corresponding "frlength" is greater * than zero gives undefined results! *------------------------------------------------------------------------*/ void usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void *ptr, usb_frlength_t len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); /* set virtual address to load and length */ xfer->frbuffers[frindex].buffer = ptr; usbd_xfer_set_frame_len(xfer, frindex, len); } void usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void **ptr, int *len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); if (ptr != NULL) *ptr = xfer->frbuffers[frindex].buffer; if (len != NULL) *len = xfer->frlengths[frindex]; } /*------------------------------------------------------------------------* * usbd_xfer_old_frame_length * * This function returns the framelength of the given frame at the * time the transfer was submitted. This function can be used to * compute the starting data pointer of the next isochronous frame * when an isochronous transfer has completed. *------------------------------------------------------------------------*/ usb_frlength_t usbd_xfer_old_frame_length(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (xfer->frlengths[frindex + xfer->max_frame_count]); } void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes) { if (actlen != NULL) *actlen = xfer->actlen; if (sumlen != NULL) *sumlen = xfer->sumlen; if (aframes != NULL) *aframes = xfer->aframes; if (nframes != NULL) *nframes = xfer->nframes; } /*------------------------------------------------------------------------* * usbd_xfer_set_frame_offset * * This function sets the frame data buffer offset relative to the beginning * of the USB DMA buffer allocated for this USB transfer. *------------------------------------------------------------------------*/ void usbd_xfer_set_frame_offset(struct usb_xfer *xfer, usb_frlength_t offset, usb_frcount_t frindex) { KASSERT(!xfer->flags.ext_buffer, ("Cannot offset data frame " "when the USB buffer is external\n")); KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); /* set virtual address to load */ xfer->frbuffers[frindex].buffer = USB_ADD_BYTES(xfer->local_buffer, offset); } void usbd_xfer_set_interval(struct usb_xfer *xfer, int i) { xfer->interval = i; } void usbd_xfer_set_timeout(struct usb_xfer *xfer, int t) { xfer->timeout = t; } void usbd_xfer_set_frames(struct usb_xfer *xfer, usb_frcount_t n) { xfer->nframes = n; } usb_frcount_t usbd_xfer_max_frames(struct usb_xfer *xfer) { return (xfer->max_frame_count); } usb_frlength_t usbd_xfer_max_len(struct usb_xfer *xfer) { return (xfer->max_data_length); } usb_frlength_t usbd_xfer_max_framelen(struct usb_xfer *xfer) { return (xfer->max_frame_size); } void usbd_xfer_set_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex, usb_frlength_t len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); xfer->frlengths[frindex] = len; } /*------------------------------------------------------------------------* * usb_callback_proc - factored out code * * This function performs USB callbacks. *------------------------------------------------------------------------*/ static void usb_callback_proc(struct usb_proc_msg *_pm) { struct usb_done_msg *pm = (void *)_pm; struct usb_xfer_root *info = pm->xroot; /* Change locking order */ USB_BUS_UNLOCK(info->bus); /* * We exploit the fact that the mutex is the same for all * callbacks that will be called from this thread: */ mtx_lock(info->xfer_mtx); USB_BUS_LOCK(info->bus); /* Continue where we lost track */ usb_command_wrapper(&info->done_q, info->done_q.curr); mtx_unlock(info->xfer_mtx); } /*------------------------------------------------------------------------* * usbd_callback_ss_done_defer * * This function will defer the start, stop and done callback to the * correct thread. *------------------------------------------------------------------------*/ static void usbd_callback_ss_done_defer(struct usb_xfer *xfer) { struct usb_xfer_root *info = xfer->xroot; struct usb_xfer_queue *pq = &info->done_q; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); if (pq->curr != xfer) { usbd_transfer_enqueue(pq, xfer); } if (!pq->recurse_1) { /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed ! */ (void) usb_proc_msignal(info->done_p, &info->done_m[0], &info->done_m[1]); } else { /* clear second recurse flag */ pq->recurse_2 = 0; } return; } /*------------------------------------------------------------------------* * usbd_callback_wrapper * * This is a wrapper for USB callbacks. This wrapper does some * auto-magic things like figuring out if we can call the callback * directly from the current context or if we need to wakeup the * interrupt process. *------------------------------------------------------------------------*/ static void usbd_callback_wrapper(struct usb_xfer_queue *pq) { struct usb_xfer *xfer = pq->curr; struct usb_xfer_root *info = xfer->xroot; USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); if ((pq->recurse_3 != 0 || mtx_owned(info->xfer_mtx) == 0) && SCHEDULER_STOPPED() == 0) { /* * Cases that end up here: * * 5) HW interrupt done callback or other source. * 6) HW completed transfer during callback */ DPRINTFN(3, "case 5 and 6\n"); /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed! * * Postponing the callback also ensures that other USB * transfer queues get a chance. */ (void) usb_proc_msignal(info->done_p, &info->done_m[0], &info->done_m[1]); return; } /* * Cases that end up here: * * 1) We are starting a transfer * 2) We are prematurely calling back a transfer * 3) We are stopping a transfer * 4) We are doing an ordinary callback */ DPRINTFN(3, "case 1-4\n"); /* get next USB transfer in the queue */ info->done_q.curr = NULL; /* set flag in case of drain */ xfer->flags_int.doing_callback = 1; USB_BUS_UNLOCK(info->bus); USB_BUS_LOCK_ASSERT(info->bus, MA_NOTOWNED); /* set correct USB state for callback */ if (!xfer->flags_int.transferring) { xfer->usb_state = USB_ST_SETUP; if (!xfer->flags_int.started) { /* we got stopped before we even got started */ USB_BUS_LOCK(info->bus); goto done; } } else { if (usbd_callback_wrapper_sub(xfer)) { /* the callback has been deferred */ USB_BUS_LOCK(info->bus); goto done; } #if USB_HAVE_POWERD /* decrement power reference */ usbd_transfer_power_ref(xfer, -1); #endif xfer->flags_int.transferring = 0; if (xfer->error) { xfer->usb_state = USB_ST_ERROR; } else { /* set transferred state */ xfer->usb_state = USB_ST_TRANSFERRED; #if USB_HAVE_BUSDMA /* sync DMA memory, if any */ if (xfer->flags_int.bdma_enable && (!xfer->flags_int.bdma_no_post_sync)) { usb_bdma_post_sync(xfer); } #endif } } #if USB_HAVE_PF if (xfer->usb_state != USB_ST_SETUP) { USB_BUS_LOCK(info->bus); usbpf_xfertap(xfer, USBPF_XFERTAP_DONE); USB_BUS_UNLOCK(info->bus); } #endif /* call processing routine */ (xfer->callback) (xfer, xfer->error); /* pickup the USB mutex again */ USB_BUS_LOCK(info->bus); /* * Check if we got started after that we got cancelled, but * before we managed to do the callback. */ if ((!xfer->flags_int.open) && (xfer->flags_int.started) && (xfer->usb_state == USB_ST_ERROR)) { /* clear flag in case of drain */ xfer->flags_int.doing_callback = 0; /* try to loop, but not recursivly */ usb_command_wrapper(&info->done_q, xfer); return; } done: /* clear flag in case of drain */ xfer->flags_int.doing_callback = 0; /* * Check if we are draining. */ if (xfer->flags_int.draining && (!xfer->flags_int.transferring)) { /* "usbd_transfer_drain()" is waiting for end of transfer */ xfer->flags_int.draining = 0; cv_broadcast(&info->cv_drain); } /* do the next callback, if any */ usb_command_wrapper(&info->done_q, info->done_q.curr); } /*------------------------------------------------------------------------* * usb_dma_delay_done_cb * * This function is called when the DMA delay has been exectuded, and * will make sure that the callback is called to complete the USB * transfer. This code path is usually only used when there is an USB * error like USB_ERR_CANCELLED. *------------------------------------------------------------------------*/ void usb_dma_delay_done_cb(struct usb_xfer *xfer) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTFN(3, "Completed %p\n", xfer); /* queue callback for execution, again */ usbd_transfer_done(xfer, 0); } /*------------------------------------------------------------------------* * usbd_transfer_dequeue * * - This function is used to remove an USB transfer from a USB * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usbd_transfer_dequeue(struct usb_xfer *xfer) { struct usb_xfer_queue *pq; pq = xfer->wait_queue; if (pq) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; } } /*------------------------------------------------------------------------* * usbd_transfer_enqueue * * - This function is used to insert an USB transfer into a USB * * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usbd_transfer_enqueue(struct usb_xfer_queue *pq, struct usb_xfer *xfer) { /* * Insert the USB transfer into the queue, if it is not * already on a USB transfer queue: */ if (xfer->wait_queue == NULL) { xfer->wait_queue = pq; TAILQ_INSERT_TAIL(&pq->head, xfer, wait_entry); } } /*------------------------------------------------------------------------* * usbd_transfer_done * * - This function is used to remove an USB transfer from the busdma, * pipe or interrupt queue. * * - This function is used to queue the USB transfer on the done * queue. * * - This function is used to stop any USB transfer timeouts. *------------------------------------------------------------------------*/ void usbd_transfer_done(struct usb_xfer *xfer, usb_error_t error) { struct usb_xfer_root *info = xfer->xroot; USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); DPRINTF("err=%s\n", usbd_errstr(error)); /* * If we are not transferring then just return. * This can happen during transfer cancel. */ if (!xfer->flags_int.transferring) { DPRINTF("not transferring\n"); /* end of control transfer, if any */ xfer->flags_int.control_act = 0; return; } /* only set transfer error, if not already set */ if (xfer->error == USB_ERR_NORMAL_COMPLETION) xfer->error = error; /* stop any callouts */ usb_callout_stop(&xfer->timeout_handle); /* * If we are waiting on a queue, just remove the USB transfer * from the queue, if any. We should have the required locks * locked to do the remove when this function is called. */ usbd_transfer_dequeue(xfer); #if USB_HAVE_BUSDMA if (mtx_owned(info->xfer_mtx)) { struct usb_xfer_queue *pq; /* * If the private USB lock is not locked, then we assume * that the BUS-DMA load stage has been passed: */ pq = &info->dma_q; if (pq->curr == xfer) { /* start the next BUS-DMA load, if any */ usb_command_wrapper(pq, NULL); } } #endif /* keep some statistics */ if (xfer->error) { info->bus->stats_err.uds_requests [xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++; } else { info->bus->stats_ok.uds_requests [xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++; } /* call the USB transfer callback */ usbd_callback_ss_done_defer(xfer); } /*------------------------------------------------------------------------* * usbd_transfer_start_cb * * This function is called to start the USB transfer when * "xfer->interval" is greater than zero, and and the endpoint type is * BULK or CONTROL. *------------------------------------------------------------------------*/ static void usbd_transfer_start_cb(void *arg) { struct usb_xfer *xfer = arg; struct usb_endpoint *ep = xfer->endpoint; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTF("start\n"); #if USB_HAVE_PF usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); #endif /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* start USB transfer, if no error */ if (xfer->error == 0) (ep->methods->start) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); } } /*------------------------------------------------------------------------* * usbd_xfer_set_stall * * This function is used to set the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usbd_xfer_set_stall(struct usb_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags.stall_pipe = 1; USB_BUS_UNLOCK(xfer->xroot->bus); } int usbd_xfer_is_stalled(struct usb_xfer *xfer) { return (xfer->endpoint->is_stalled); } /*------------------------------------------------------------------------* * usbd_transfer_clear_stall * * This function is used to clear the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usbd_transfer_clear_stall(struct usb_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags.stall_pipe = 0; USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_pipe_start * * This function is used to add an USB transfer to the pipe transfer list. *------------------------------------------------------------------------*/ void usbd_pipe_start(struct usb_xfer_queue *pq) { struct usb_endpoint *ep; struct usb_xfer *xfer; uint8_t type; xfer = pq->curr; ep = xfer->endpoint; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* * If the endpoint is already stalled we do nothing ! */ if (ep->is_stalled) { return; } /* * Check if we are supposed to stall the endpoint: */ if (xfer->flags.stall_pipe) { struct usb_device *udev; struct usb_xfer_root *info; /* clear stall command */ xfer->flags.stall_pipe = 0; /* get pointer to USB device */ info = xfer->xroot; udev = info->udev; /* * Only stall BULK and INTERRUPT endpoints. */ type = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_INTERRUPT)) { uint8_t did_stall; did_stall = 1; if (udev->flags.usb_mode == USB_MODE_DEVICE) { (udev->bus->methods->set_stall) ( udev, ep, &did_stall); } else if (udev->ctrl_xfer[1]) { info = udev->ctrl_xfer[1]->xroot; usb_proc_msignal( USB_BUS_CS_PROC(info->bus), &udev->cs_msg[0], &udev->cs_msg[1]); } else { /* should not happen */ DPRINTFN(0, "No stall handler\n"); } /* * Check if we should stall. Some USB hardware * handles set- and clear-stall in hardware. */ if (did_stall) { /* * The transfer will be continued when * the clear-stall control endpoint * message is received. */ ep->is_stalled = 1; return; } } else if (type == UE_ISOCHRONOUS) { /* * Make sure any FIFO overflow or other FIFO * error conditions go away by resetting the * endpoint FIFO through the clear stall * method. */ if (udev->flags.usb_mode == USB_MODE_DEVICE) { (udev->bus->methods->clear_stall) (udev, ep); } } } /* Set or clear stall complete - special case */ if (xfer->nframes == 0) { /* we are complete */ xfer->aframes = 0; usbd_transfer_done(xfer, 0); return; } /* * Handled cases: * * 1) Start the first transfer queued. * * 2) Re-start the current USB transfer. */ /* * Check if there should be any * pre transfer start delay: */ if (xfer->interval > 0) { type = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_CONTROL)) { usbd_transfer_timeout_ms(xfer, &usbd_transfer_start_cb, xfer->interval); return; } } DPRINTF("start\n"); #if USB_HAVE_PF usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); #endif /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* start USB transfer, if no error */ if (xfer->error == 0) (ep->methods->start) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); } } /*------------------------------------------------------------------------* * usbd_transfer_timeout_ms * * This function is used to setup a timeout on the given USB * transfer. If the timeout has been deferred the callback given by * "cb" will get called after "ms" milliseconds. *------------------------------------------------------------------------*/ void usbd_transfer_timeout_ms(struct usb_xfer *xfer, void (*cb) (void *arg), usb_timeout_t ms) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* defer delay */ usb_callout_reset(&xfer->timeout_handle, USB_MS_TO_TICKS(ms) + USB_CALLOUT_ZERO_TICKS, cb, xfer); } /*------------------------------------------------------------------------* * usbd_callback_wrapper_sub * * - This function will update variables in an USB transfer after * that the USB transfer is complete. * * - This function is used to start the next USB transfer on the * ep transfer queue, if any. * * NOTE: In some special cases the USB transfer will not be removed from * the pipe queue, but remain first. To enforce USB transfer removal call * this function passing the error code "USB_ERR_CANCELLED". * * Return values: * 0: Success. * Else: The callback has been deferred. *------------------------------------------------------------------------*/ static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *xfer) { struct usb_endpoint *ep; struct usb_bus *bus; usb_frcount_t x; bus = xfer->xroot->bus; if ((!xfer->flags_int.open) && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); USB_BUS_LOCK(bus); (xfer->endpoint->methods->close) (xfer); USB_BUS_UNLOCK(bus); /* only close once */ xfer->flags_int.did_close = 1; return (1); /* wait for new callback */ } /* * If we have a non-hardware induced error we * need to do the DMA delay! */ if (xfer->error != 0 && !xfer->flags_int.did_dma_delay && (xfer->error == USB_ERR_CANCELLED || xfer->error == USB_ERR_TIMEOUT || bus->methods->start_dma_delay != NULL)) { usb_timeout_t temp; /* only delay once */ xfer->flags_int.did_dma_delay = 1; /* we can not cancel this delay */ xfer->flags_int.can_cancel_immed = 0; temp = usbd_get_dma_delay(xfer->xroot->udev); DPRINTFN(3, "DMA delay, %u ms, " "on %p\n", temp, xfer); if (temp != 0) { USB_BUS_LOCK(bus); /* * Some hardware solutions have dedicated * events when it is safe to free DMA'ed * memory. For the other hardware platforms we * use a static delay. */ if (bus->methods->start_dma_delay != NULL) { (bus->methods->start_dma_delay) (xfer); } else { usbd_transfer_timeout_ms(xfer, (void (*)(void *))&usb_dma_delay_done_cb, temp); } USB_BUS_UNLOCK(bus); return (1); /* wait for new callback */ } } /* check actual number of frames */ if (xfer->aframes > xfer->nframes) { if (xfer->error == 0) { panic("%s: actual number of frames, %d, is " "greater than initial number of frames, %d\n", __FUNCTION__, xfer->aframes, xfer->nframes); } else { /* just set some valid value */ xfer->aframes = xfer->nframes; } } /* compute actual length */ xfer->actlen = 0; for (x = 0; x != xfer->aframes; x++) { xfer->actlen += xfer->frlengths[x]; } /* * Frames that were not transferred get zero actual length in * case the USB device driver does not check the actual number * of frames transferred, "xfer->aframes": */ for (; x < xfer->nframes; x++) { usbd_xfer_set_frame_len(xfer, x, 0); } /* check actual length */ if (xfer->actlen > xfer->sumlen) { if (xfer->error == 0) { panic("%s: actual length, %d, is greater than " "initial length, %d\n", __FUNCTION__, xfer->actlen, xfer->sumlen); } else { /* just set some valid value */ xfer->actlen = xfer->sumlen; } } DPRINTFN(1, "xfer=%p endpoint=%p sts=%d alen=%d, slen=%d, afrm=%d, nfrm=%d\n", xfer, xfer->endpoint, xfer->error, xfer->actlen, xfer->sumlen, xfer->aframes, xfer->nframes); if (xfer->error) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; #if USB_HAVE_TT_SUPPORT switch (xfer->error) { case USB_ERR_NORMAL_COMPLETION: case USB_ERR_SHORT_XFER: case USB_ERR_STALLED: case USB_ERR_CANCELLED: /* nothing to do */ break; default: /* try to reset the TT, if any */ USB_BUS_LOCK(bus); uhub_tt_buffer_reset_async_locked(xfer->xroot->udev, xfer->endpoint); USB_BUS_UNLOCK(bus); break; } #endif /* check if we should block the execution queue */ if ((xfer->error != USB_ERR_CANCELLED) && (xfer->flags.pipe_bof)) { DPRINTFN(2, "xfer=%p: Block On Failure " "on endpoint=%p\n", xfer, xfer->endpoint); goto done; } } else { /* check for short transfers */ if (xfer->actlen < xfer->sumlen) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; if (!xfer->flags_int.short_xfer_ok) { xfer->error = USB_ERR_SHORT_XFER; if (xfer->flags.pipe_bof) { DPRINTFN(2, "xfer=%p: Block On Failure on " "Short Transfer on endpoint %p.\n", xfer, xfer->endpoint); goto done; } } } else { /* * Check if we are in the middle of a * control transfer: */ if (xfer->flags_int.control_act) { DPRINTFN(5, "xfer=%p: Control transfer " "active on endpoint=%p\n", xfer, xfer->endpoint); goto done; } } } ep = xfer->endpoint; /* * If the current USB transfer is completing we need to start the * next one: */ USB_BUS_LOCK(bus); if (ep->endpoint_q[xfer->stream_id].curr == xfer) { usb_command_wrapper(&ep->endpoint_q[xfer->stream_id], NULL); if (ep->endpoint_q[xfer->stream_id].curr != NULL || TAILQ_FIRST(&ep->endpoint_q[xfer->stream_id].head) != NULL) { /* there is another USB transfer waiting */ } else { /* this is the last USB transfer */ /* clear isochronous sync flag */ xfer->endpoint->is_synced = 0; } } USB_BUS_UNLOCK(bus); done: return (0); } /*------------------------------------------------------------------------* * usb_command_wrapper * * This function is used to execute commands non-recursivly on an USB * transfer. *------------------------------------------------------------------------*/ void usb_command_wrapper(struct usb_xfer_queue *pq, struct usb_xfer *xfer) { if (xfer) { /* * If the transfer is not already processing, * queue it! */ if (pq->curr != xfer) { usbd_transfer_enqueue(pq, xfer); if (pq->curr != NULL) { /* something is already processing */ DPRINTFN(6, "busy %p\n", pq->curr); return; } } } else { /* Get next element in queue */ pq->curr = NULL; } if (!pq->recurse_1) { /* clear third recurse flag */ pq->recurse_3 = 0; do { /* set two first recurse flags */ pq->recurse_1 = 1; pq->recurse_2 = 1; if (pq->curr == NULL) { xfer = TAILQ_FIRST(&pq->head); if (xfer) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; pq->curr = xfer; } else { break; } } DPRINTFN(6, "cb %p (enter)\n", pq->curr); (pq->command) (pq); DPRINTFN(6, "cb %p (leave)\n", pq->curr); /* * Set third recurse flag to indicate * recursion happened: */ pq->recurse_3 = 1; } while (!pq->recurse_2); /* clear first recurse flag */ pq->recurse_1 = 0; } else { /* clear second recurse flag */ pq->recurse_2 = 0; } } /*------------------------------------------------------------------------* * usbd_ctrl_transfer_setup * * This function is used to setup the default USB control endpoint * transfer. *------------------------------------------------------------------------*/ void usbd_ctrl_transfer_setup(struct usb_device *udev) { struct usb_xfer *xfer; uint8_t no_resetup; uint8_t iface_index; /* check for root HUB */ if (udev->parent_hub == NULL) return; repeat: xfer = udev->ctrl_xfer[0]; if (xfer) { USB_XFER_LOCK(xfer); no_resetup = ((xfer->address == udev->address) && (udev->ctrl_ep_desc.wMaxPacketSize[0] == udev->ddesc.bMaxPacketSize)); if (udev->flags.usb_mode == USB_MODE_DEVICE) { if (no_resetup) { /* * NOTE: checking "xfer->address" and * starting the USB transfer must be * atomic! */ usbd_transfer_start(xfer); } } USB_XFER_UNLOCK(xfer); } else { no_resetup = 0; } if (no_resetup) { /* * All parameters are exactly the same like before. * Just return. */ return; } /* * Update wMaxPacketSize for the default control endpoint: */ udev->ctrl_ep_desc.wMaxPacketSize[0] = udev->ddesc.bMaxPacketSize; /* * Unsetup any existing USB transfer: */ usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); /* * Reset clear stall error counter. */ udev->clear_stall_errors = 0; /* * Try to setup a new USB transfer for the * default control endpoint: */ iface_index = 0; if (usbd_transfer_setup(udev, &iface_index, udev->ctrl_xfer, usb_control_ep_cfg, USB_CTRL_XFER_MAX, NULL, &udev->device_mtx)) { DPRINTFN(0, "could not setup default " "USB transfer\n"); } else { goto repeat; } } /*------------------------------------------------------------------------* * usbd_clear_data_toggle - factored out code * * NOTE: the intention of this function is not to reset the hardware * data toggle. *------------------------------------------------------------------------*/ void usbd_clear_stall_locked(struct usb_device *udev, struct usb_endpoint *ep) { USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); /* check that we have a valid case */ if (udev->flags.usb_mode == USB_MODE_HOST && udev->parent_hub != NULL && udev->bus->methods->clear_stall != NULL && ep->methods != NULL) { (udev->bus->methods->clear_stall) (udev, ep); } } /*------------------------------------------------------------------------* * usbd_clear_data_toggle - factored out code * * NOTE: the intention of this function is not to reset the hardware * data toggle on the USB device side. *------------------------------------------------------------------------*/ void usbd_clear_data_toggle(struct usb_device *udev, struct usb_endpoint *ep) { DPRINTFN(5, "udev=%p endpoint=%p\n", udev, ep); USB_BUS_LOCK(udev->bus); ep->toggle_next = 0; /* some hardware needs a callback to clear the data toggle */ usbd_clear_stall_locked(udev, ep); USB_BUS_UNLOCK(udev->bus); } /*------------------------------------------------------------------------* * usbd_clear_stall_callback - factored out clear stall callback * * Input parameters: * xfer1: Clear Stall Control Transfer * xfer2: Stalled USB Transfer * * This function is NULL safe. * * Return values: * 0: In progress * Else: Finished * * Clear stall config example: * * static const struct usb_config my_clearstall = { * .type = UE_CONTROL, * .endpoint = 0, * .direction = UE_DIR_ANY, * .interval = 50, //50 milliseconds * .bufsize = sizeof(struct usb_device_request), * .timeout = 1000, //1.000 seconds * .callback = &my_clear_stall_callback, // ** * .usb_mode = USB_MODE_HOST, * }; * * ** "my_clear_stall_callback" calls "usbd_clear_stall_callback" * passing the correct parameters. *------------------------------------------------------------------------*/ uint8_t usbd_clear_stall_callback(struct usb_xfer *xfer1, struct usb_xfer *xfer2) { struct usb_device_request req; if (xfer2 == NULL) { /* looks like we are tearing down */ DPRINTF("NULL input parameter\n"); return (0); } USB_XFER_LOCK_ASSERT(xfer1, MA_OWNED); USB_XFER_LOCK_ASSERT(xfer2, MA_OWNED); switch (USB_GET_STATE(xfer1)) { case USB_ST_SETUP: /* * pre-clear the data toggle to DATA0 ("umass.c" and * "ata-usb.c" depends on this) */ usbd_clear_data_toggle(xfer2->xroot->udev, xfer2->endpoint); /* setup a clear-stall packet */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); req.wIndex[0] = xfer2->endpoint->edesc->bEndpointAddress; req.wIndex[1] = 0; USETW(req.wLength, 0); /* * "usbd_transfer_setup_sub()" will ensure that * we have sufficient room in the buffer for * the request structure! */ /* copy in the transfer */ usbd_copy_in(xfer1->frbuffers, 0, &req, sizeof(req)); /* set length */ xfer1->frlengths[0] = sizeof(req); xfer1->nframes = 1; usbd_transfer_submit(xfer1); return (0); case USB_ST_TRANSFERRED: break; default: /* Error */ if (xfer1->error == USB_ERR_CANCELLED) { return (0); } break; } return (1); /* Clear Stall Finished */ } /*------------------------------------------------------------------------* * usbd_transfer_poll * * The following function gets called from the USB keyboard driver and * UMASS when the system has paniced. * * NOTE: It is currently not possible to resume normal operation on * the USB controller which has been polled, due to clearing of the * "up_dsleep" and "up_msleep" flags. *------------------------------------------------------------------------*/ void usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max) { struct usb_xfer *xfer; struct usb_xfer_root *xroot; struct usb_device *udev; struct usb_proc_msg *pm; uint16_t n; uint16_t drop_bus; uint16_t drop_xfer; for (n = 0; n != max; n++) { /* Extra checks to avoid panic */ xfer = ppxfer[n]; if (xfer == NULL) continue; /* no USB transfer */ xroot = xfer->xroot; if (xroot == NULL) continue; /* no USB root */ udev = xroot->udev; if (udev == NULL) continue; /* no USB device */ if (udev->bus == NULL) continue; /* no BUS structure */ if (udev->bus->methods == NULL) continue; /* no BUS methods */ if (udev->bus->methods->xfer_poll == NULL) continue; /* no poll method */ /* make sure that the BUS mutex is not locked */ drop_bus = 0; while (mtx_owned(&xroot->udev->bus->bus_mtx) && !SCHEDULER_STOPPED()) { mtx_unlock(&xroot->udev->bus->bus_mtx); drop_bus++; } /* make sure that the transfer mutex is not locked */ drop_xfer = 0; while (mtx_owned(xroot->xfer_mtx) && !SCHEDULER_STOPPED()) { mtx_unlock(xroot->xfer_mtx); drop_xfer++; } /* Make sure cv_signal() and cv_broadcast() is not called */ USB_BUS_CONTROL_XFER_PROC(udev->bus)->up_msleep = 0; USB_BUS_EXPLORE_PROC(udev->bus)->up_msleep = 0; USB_BUS_GIANT_PROC(udev->bus)->up_msleep = 0; USB_BUS_NON_GIANT_ISOC_PROC(udev->bus)->up_msleep = 0; USB_BUS_NON_GIANT_BULK_PROC(udev->bus)->up_msleep = 0; /* poll USB hardware */ (udev->bus->methods->xfer_poll) (udev->bus); USB_BUS_LOCK(xroot->bus); /* check for clear stall */ if (udev->ctrl_xfer[1] != NULL) { /* poll clear stall start */ pm = &udev->cs_msg[0].hdr; (pm->pm_callback) (pm); /* poll clear stall done thread */ pm = &udev->ctrl_xfer[1]-> xroot->done_m[0].hdr; (pm->pm_callback) (pm); } /* poll done thread */ pm = &xroot->done_m[0].hdr; (pm->pm_callback) (pm); USB_BUS_UNLOCK(xroot->bus); /* restore transfer mutex */ while (drop_xfer--) mtx_lock(xroot->xfer_mtx); /* restore BUS mutex */ while (drop_bus--) mtx_lock(&xroot->udev->bus->bus_mtx); } } static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr, uint8_t type, enum usb_dev_speed speed) { static const uint16_t intr_range_max[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 64, [USB_SPEED_HIGH] = 1024, [USB_SPEED_VARIABLE] = 1024, [USB_SPEED_SUPER] = 1024, }; static const uint16_t isoc_range_max[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 0, /* invalid */ [USB_SPEED_FULL] = 1023, [USB_SPEED_HIGH] = 1024, [USB_SPEED_VARIABLE] = 3584, [USB_SPEED_SUPER] = 1024, }; static const uint16_t control_min[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 8, [USB_SPEED_HIGH] = 64, [USB_SPEED_VARIABLE] = 512, [USB_SPEED_SUPER] = 512, }; static const uint16_t bulk_min[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 8, [USB_SPEED_HIGH] = 512, [USB_SPEED_VARIABLE] = 512, [USB_SPEED_SUPER] = 1024, }; uint16_t temp; memset(ptr, 0, sizeof(*ptr)); switch (type) { case UE_INTERRUPT: ptr->range.max = intr_range_max[speed]; break; case UE_ISOCHRONOUS: ptr->range.max = isoc_range_max[speed]; break; default: if (type == UE_BULK) temp = bulk_min[speed]; else /* UE_CONTROL */ temp = control_min[speed]; /* default is fixed */ ptr->fixed[0] = temp; ptr->fixed[1] = temp; ptr->fixed[2] = temp; ptr->fixed[3] = temp; if (speed == USB_SPEED_FULL) { /* multiple sizes */ ptr->fixed[1] = 16; ptr->fixed[2] = 32; ptr->fixed[3] = 64; } if ((speed == USB_SPEED_VARIABLE) && (type == UE_BULK)) { /* multiple sizes */ ptr->fixed[2] = 1024; ptr->fixed[3] = 1536; } break; } } void * usbd_xfer_softc(struct usb_xfer *xfer) { return (xfer->priv_sc); } void * usbd_xfer_get_priv(struct usb_xfer *xfer) { return (xfer->priv_fifo); } void usbd_xfer_set_priv(struct usb_xfer *xfer, void *ptr) { xfer->priv_fifo = ptr; } uint8_t usbd_xfer_state(struct usb_xfer *xfer) { return (xfer->usb_state); } void usbd_xfer_set_flag(struct usb_xfer *xfer, int flag) { switch (flag) { case USB_FORCE_SHORT_XFER: xfer->flags.force_short_xfer = 1; break; case USB_SHORT_XFER_OK: xfer->flags.short_xfer_ok = 1; break; case USB_MULTI_SHORT_OK: xfer->flags.short_frames_ok = 1; break; case USB_MANUAL_STATUS: xfer->flags.manual_status = 1; break; } } void usbd_xfer_clr_flag(struct usb_xfer *xfer, int flag) { switch (flag) { case USB_FORCE_SHORT_XFER: xfer->flags.force_short_xfer = 0; break; case USB_SHORT_XFER_OK: xfer->flags.short_xfer_ok = 0; break; case USB_MULTI_SHORT_OK: xfer->flags.short_frames_ok = 0; break; case USB_MANUAL_STATUS: xfer->flags.manual_status = 0; break; } } /* * The following function returns in milliseconds when the isochronous * transfer was completed by the hardware. The returned value wraps * around 65536 milliseconds. */ uint16_t usbd_xfer_get_timestamp(struct usb_xfer *xfer) { return (xfer->isoc_time_complete); } /* * The following function returns non-zero if the max packet size * field was clamped to a valid value. Else it returns zero. */ uint8_t usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer) { return (xfer->flags_int.maxp_was_clamped); } Index: projects/clang390-import/sys/dev/usb/usb_util.c =================================================================== --- projects/clang390-import/sys/dev/usb/usb_util.c (revision 305430) +++ projects/clang390-import/sys/dev/usb/usb_util.c (revision 305431) @@ -1,223 +1,223 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ /*------------------------------------------------------------------------* * device_set_usb_desc * * This function can be called at probe or attach to set the USB * device supplied textual description for the given device. *------------------------------------------------------------------------*/ void device_set_usb_desc(device_t dev) { struct usb_attach_arg *uaa; struct usb_device *udev; struct usb_interface *iface; char *temp_p; usb_error_t err; uint8_t do_unlock; if (dev == NULL) { /* should not happen */ return; } uaa = device_get_ivars(dev); if (uaa == NULL) { /* can happen if called at the wrong time */ return; } udev = uaa->device; iface = uaa->iface; if ((iface == NULL) || (iface->idesc == NULL) || (iface->idesc->iInterface == 0)) { err = USB_ERR_INVAL; } else { err = 0; } /* Protect scratch area */ - do_unlock = usbd_enum_lock(udev); + do_unlock = usbd_ctrl_lock(udev); temp_p = (char *)udev->scratch.data; if (err == 0) { /* try to get the interface string ! */ err = usbd_req_get_string_any(udev, NULL, temp_p, sizeof(udev->scratch.data), iface->idesc->iInterface); } if (err != 0) { /* use default description */ usb_devinfo(udev, temp_p, sizeof(udev->scratch.data)); } if (do_unlock) - usbd_enum_unlock(udev); + usbd_ctrl_unlock(udev); device_set_desc_copy(dev, temp_p); device_printf(dev, "<%s> on %s\n", temp_p, device_get_nameunit(udev->bus->bdev)); } /*------------------------------------------------------------------------* * usb_pause_mtx - factored out code * * This function will delay the code by the passed number of system * ticks. The passed mutex "mtx" will be dropped while waiting, if * "mtx" is different from NULL. *------------------------------------------------------------------------*/ void usb_pause_mtx(struct mtx *mtx, int timo) { if (mtx != NULL) mtx_unlock(mtx); /* * Add one tick to the timeout so that we don't return too * early! Note that pause() will assert that the passed * timeout is positive and non-zero! */ pause("USBWAIT", timo + 1); if (mtx != NULL) mtx_lock(mtx); } /*------------------------------------------------------------------------* * usb_printbcd * * This function will print the version number "bcd" to the string * pointed to by "p" having a maximum length of "p_len" bytes * including the terminating zero. *------------------------------------------------------------------------*/ void usb_printbcd(char *p, uint16_t p_len, uint16_t bcd) { if (snprintf(p, p_len, "%x.%02x", bcd >> 8, bcd & 0xff)) { /* ignore any errors */ } } /*------------------------------------------------------------------------* * usb_trim_spaces * * This function removes spaces at the beginning and the end of the string * pointed to by the "p" argument. *------------------------------------------------------------------------*/ void usb_trim_spaces(char *p) { char *q; char *e; if (p == NULL) return; q = e = p; while (*q == ' ') /* skip leading spaces */ q++; while ((*p = *q++)) /* copy string */ if (*p++ != ' ') /* remember last non-space */ e = p; *e = 0; /* kill trailing spaces */ } /*------------------------------------------------------------------------* * usb_make_str_desc - convert an ASCII string into a UNICODE string *------------------------------------------------------------------------*/ uint8_t usb_make_str_desc(void *ptr, uint16_t max_len, const char *s) { struct usb_string_descriptor *p = ptr; uint8_t totlen; int j; if (max_len < 2) { /* invalid length */ return (0); } max_len = ((max_len / 2) - 1); j = strlen(s); if (j < 0) { j = 0; } if (j > 126) { j = 126; } if (max_len > j) { max_len = j; } totlen = (max_len + 1) * 2; p->bLength = totlen; p->bDescriptorType = UDESC_STRING; while (max_len--) { USETW2(p->bString[max_len], 0, s[max_len]); } return (totlen); } Index: projects/clang390-import/sys/mips/broadcom/files.broadcom =================================================================== --- projects/clang390-import/sys/mips/broadcom/files.broadcom (revision 305430) +++ projects/clang390-import/sys/mips/broadcom/files.broadcom (revision 305431) @@ -1,21 +1,26 @@ # $FreeBSD$ # TODO: Add attachment elsewhere in the tree # for USB 1.1 OHCI, Ethernet and IPSEC cores # which are believed to be devices we have drivers for # which just need to be tweaked for attachment to an BHND system bus. mips/broadcom/bcm_machdep.c standard mips/broadcom/bcm_pmu.c standard mips/mips/tick.c standard mips/mips/mips_pic.c standard kern/subr_intr.c standard kern/pic_if.m standard kern/msi_if.m optional intrng mips/broadcom/uart_cpu_chipc.c optional uart mips/broadcom/uart_bus_chipc.c optional uart mips/broadcom/bcm_mipscore.c standard # TODO: Replace with BCM47xx/57xx/etc-aware geom_map geom/geom_flashmap.c standard + +# USB bits +dev/bhnd/cores/usb/bhnd_usb.c optional usb +dev/bhnd/cores/usb/bhnd_ehci.c optional ehci +dev/bhnd/cores/usb/bhnd_ohci.c optional ohci \ No newline at end of file Index: projects/clang390-import/sys/mips/conf/BCM =================================================================== --- projects/clang390-import/sys/mips/conf/BCM (revision 305430) +++ projects/clang390-import/sys/mips/conf/BCM (revision 305431) @@ -1,101 +1,109 @@ # # $FreeBSD$ # # The Broadcom 470x/471x/535x series of processors and boards is very commonly # used in COTS hardware including the ASUS RT-N12, RT-N16, RT-N53. # ident BCM cpu CPU_MIPS74K hints "BCM.hints" include "../broadcom/std.broadcom" # ships with cfe firmware options CFE device cfe options ALT_BREAK_TO_DEBUGGER options BREAK_TO_DEBUGGER options BOOTVERBOSE=0 makeoptions TRAMPLOADADDR=0x80800000 makeoptions DEBUG="-g3" #Build kernel with gdb(1) debug symbols makeoptions MODULES_OVERRIDE="" options DDB options KDB options SCHED_4BSD #4BSD scheduler options INET #InterNETworking options NFSCL #Network Filesystem Client #options NFS_ROOT #NFS usable as /, requires NFSCL options PSEUDOFS #Pseudo-filesystem framework options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions options FFS #Berkeley Fast Filesystem options SOFTUPDATES #Enable FFS soft updates support options UFS_ACL #Support for access control lists options UFS_DIRHASH #Improve performance on big directories device geom_uzip options GEOM_UZIP options GEOM_LABEL # Providers labelization. options ROOTDEVNAME=\"ufs:ufs/FBSD\" # assumes FW built by # freebsd-build-wifi # Debugging for use in -current #options DEADLKRES options INVARIANTS options INVARIANT_SUPPORT #options BHND_LOGLEVEL=BHND_DEBUG_LEVEL #options BUS_DEBUG #makeoptions BUS_DEBUG options EARLY_PRINTF #options VERBOSE_SYSINIT #makeoptions VERBOSE_SYSINIT # bhnd(4) device bhnd device bcma # bcma backplane device bcma_nexus device pci device bhnd_pcib # PCIe-G1 core #device bgmac # Broadcom GMAC - not yet device mdio #Flash device spibus device mx25l # Serial Flash device cfi # Parallel Flash device cfid #UART device uart #Base device loop device ether device random device md #Performance #options HWPMC_HOOKS #device hwpmc #device hwpmc_mips74k #Ethernet # device bfe # XXX will build both pci and siba device miibus # attachments # pci devices -# USB is not yet ready -#options USB_DEBUG # enable debug msgs -#device usb # USB Bus (required) -#device uhci # UHCI PCI->USB interface -#device ehci # EHCI PCI->USB interface (USB 2.0) +# USB +options USB_DEBUG # enable debug msgs +# taken from atheros +options USB_EHCI_BIG_ENDIAN_DESC # handle big-endian byte order +options USB_HOST_ALIGN=32 # AR71XX (MIPS in general?) requires this + +device usb # USB Bus (required) +device ohci # OHCI interface +device ehci # EHCI interface (USB 2.0) + +device scbus +device umass +device da Index: projects/clang390-import/sys/net/rndis.h =================================================================== --- projects/clang390-import/sys/net/rndis.h (revision 305430) +++ projects/clang390-import/sys/net/rndis.h (revision 305431) @@ -1,337 +1,356 @@ /* $FreeBSD$ */ /* $OpenBSD: if_urndisreg.h,v 1.19 2013/11/21 14:08:05 mpi Exp $ */ /* * Copyright (c) 2010 Jonathan Armani * Copyright (c) 2010 Fabien Romano * Copyright (c) 2010 Michael Knudsen * All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _NET_RNDIS_H_ #define _NET_RNDIS_H_ /* Canonical major/minor version as of 22th Aug. 2016. */ #define RNDIS_VERSION_MAJOR 0x00000001 #define RNDIS_VERSION_MINOR 0x00000000 #define RNDIS_STATUS_SUCCESS 0x00000000L #define RNDIS_STATUS_PENDING 0x00000103L #define RNDIS_STATUS_MEDIA_CONNECT 0x4001000BL #define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000CL #define RNDIS_STATUS_BUFFER_OVERFLOW 0x80000005L #define RNDIS_STATUS_FAILURE 0xC0000001L #define RNDIS_STATUS_NOT_SUPPORTED 0xC00000BBL #define RNDIS_STATUS_RESOURCES 0xC000009AL #define RNDIS_STATUS_INVALID_DATA 0xC0010015L #define OID_GEN_SUPPORTED_LIST 0x00010101 #define OID_GEN_HARDWARE_STATUS 0x00010102 #define OID_GEN_MEDIA_SUPPORTED 0x00010103 #define OID_GEN_MEDIA_IN_USE 0x00010104 #define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 #define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 #define OID_GEN_LINK_SPEED 0x00010107 #define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 #define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 #define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A #define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B #define OID_GEN_VENDOR_ID 0x0001010C #define OID_GEN_VENDOR_DESCRIPTION 0x0001010D #define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E #define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F #define OID_GEN_DRIVER_VERSION 0x00010110 #define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 #define OID_GEN_PROTOCOL_OPTIONS 0x00010112 #define OID_GEN_MAC_OPTIONS 0x00010113 #define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 #define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 #define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 #define OID_GEN_SUPPORTED_GUIDS 0x00010117 #define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 #define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 #define OID_GEN_RECEIVE_SCALE_CAPABILITIES 0x00010203 #define OID_GEN_RECEIVE_SCALE_PARAMETERS 0x00010204 #define OID_GEN_MACHINE_NAME 0x0001021A #define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B #define OID_GEN_VLAN_ID 0x0001021C #define OID_802_3_PERMANENT_ADDRESS 0x01010101 #define OID_802_3_CURRENT_ADDRESS 0x01010102 #define OID_802_3_MULTICAST_LIST 0x01010103 #define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 #define OID_802_3_MAC_OPTIONS 0x01010105 #define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 #define OID_802_3_XMIT_ONE_COLLISION 0x01020102 #define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 #define OID_802_3_XMIT_DEFERRED 0x01020201 #define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 #define OID_802_3_RCV_OVERRUN 0x01020203 #define OID_802_3_XMIT_UNDERRUN 0x01020204 #define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 #define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 #define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 #define OID_TCP_OFFLOAD_PARAMETERS 0xFC01020C #define RNDIS_MEDIUM_802_3 0x00000000 /* Device flags */ #define RNDIS_DF_CONNECTIONLESS 0x00000001 #define RNDIS_DF_CONNECTION_ORIENTED 0x00000002 /* * Common RNDIS message header. */ struct rndis_msghdr { uint32_t rm_type; uint32_t rm_len; }; /* * RNDIS data message */ #define REMOTE_NDIS_PACKET_MSG 0x00000001 struct rndis_packet_msg { uint32_t rm_type; uint32_t rm_len; uint32_t rm_dataoffset; uint32_t rm_datalen; uint32_t rm_oobdataoffset; uint32_t rm_oobdatalen; uint32_t rm_oobdataelements; uint32_t rm_pktinfooffset; uint32_t rm_pktinfolen; uint32_t rm_vchandle; uint32_t rm_reserved; }; /* * Minimum value for rm_dataoffset, rm_oobdataoffset, and * rm_pktinfooffset. */ #define RNDIS_PACKET_MSG_OFFSET_MIN \ (sizeof(struct rndis_packet_msg) - \ __offsetof(struct rndis_packet_msg, rm_dataoffset)) /* Per-packet-info for RNDIS data message */ struct rndis_pktinfo { uint32_t rm_size; uint32_t rm_type; /* NDIS_PKTINFO_TYPE_ */ uint32_t rm_pktinfooffset; uint8_t rm_data[]; }; #define RNDIS_PKTINFO_OFFSET \ __offsetof(struct rndis_pktinfo, rm_data[0]) #define RNDIS_PKTINFO_ALIGN 4 #define NDIS_PKTINFO_TYPE_CSUM 0 #define NDIS_PKTINFO_TYPE_IPSEC 1 #define NDIS_PKTINFO_TYPE_LSO 2 #define NDIS_PKTINFO_TYPE_CLASSIFY 3 /* reserved 4 */ #define NDIS_PKTINFO_TYPE_SGLIST 5 #define NDIS_PKTINFO_TYPE_VLAN 6 #define NDIS_PKTINFO_TYPE_ORIG 7 #define NDIS_PKTINFO_TYPE_PKT_CANCELID 8 #define NDIS_PKTINFO_TYPE_ORIG_NBLIST 9 #define NDIS_PKTINFO_TYPE_CACHE_NBLIST 10 #define NDIS_PKTINFO_TYPE_PKT_PAD 11 /* * RNDIS control messages */ /* * Common header for RNDIS completion messages. * * NOTE: It does not apply to REMOTE_NDIS_RESET_CMPLT. */ struct rndis_comp_hdr { uint32_t rm_type; uint32_t rm_len; uint32_t rm_rid; uint32_t rm_status; }; /* Initialize the device. */ #define REMOTE_NDIS_INITIALIZE_MSG 0x00000002 #define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002 struct rndis_init_req { uint32_t rm_type; uint32_t rm_len; uint32_t rm_rid; uint32_t rm_ver_major; uint32_t rm_ver_minor; uint32_t rm_max_xfersz; }; struct rndis_init_comp { uint32_t rm_type; uint32_t rm_len; uint32_t rm_rid; uint32_t rm_status; uint32_t rm_ver_major; uint32_t rm_ver_minor; uint32_t rm_devflags; uint32_t rm_medium; uint32_t rm_pktmaxcnt; uint32_t rm_pktmaxsz; uint32_t rm_align; uint32_t rm_aflistoffset; uint32_t rm_aflistsz; }; #define RNDIS_INIT_COMP_SIZE_MIN \ __offsetof(struct rndis_init_comp, rm_aflistsz) /* Halt the device. No response sent. */ #define REMOTE_NDIS_HALT_MSG 0x00000003 struct rndis_halt_req { uint32_t rm_type; uint32_t rm_len; uint32_t rm_rid; }; /* Send a query object. */ #define REMOTE_NDIS_QUERY_MSG 0x00000004 #define REMOTE_NDIS_QUERY_CMPLT 0x80000004 struct rndis_query_req { uint32_t rm_type; uint32_t rm_len; uint32_t rm_rid; uint32_t rm_oid; uint32_t rm_infobuflen; uint32_t rm_infobufoffset; uint32_t rm_devicevchdl; }; #define RNDIS_QUERY_REQ_INFOBUFOFFSET \ (sizeof(struct rndis_query_req) - \ __offsetof(struct rndis_query_req, rm_rid)) struct rndis_query_comp { uint32_t rm_type; uint32_t rm_len; uint32_t rm_rid; uint32_t rm_status; uint32_t rm_infobuflen; uint32_t rm_infobufoffset; }; #define RNDIS_QUERY_COMP_INFOBUFABS(ofs) \ ((ofs) + __offsetof(struct rndis_query_req, rm_rid)) /* Send a set object request. */ #define REMOTE_NDIS_SET_MSG 0x00000005 #define REMOTE_NDIS_SET_CMPLT 0x80000005 struct rndis_set_req { uint32_t rm_type; uint32_t rm_len; uint32_t rm_rid; uint32_t rm_oid; uint32_t rm_infobuflen; uint32_t rm_infobufoffset; uint32_t rm_devicevchdl; }; #define RNDIS_SET_REQ_INFOBUFOFFSET \ (sizeof(struct rndis_set_req) - \ __offsetof(struct rndis_set_req, rm_rid)) struct rndis_set_comp { uint32_t rm_type; uint32_t rm_len; uint32_t rm_rid; uint32_t rm_status; }; /* * Parameter used by OID_GEN_RNDIS_CONFIG_PARAMETER. */ #define REMOTE_NDIS_SET_PARAM_NUMERIC 0x00000000 #define REMOTE_NDIS_SET_PARAM_STRING 0x00000002 struct rndis_set_parameter { uint32_t rm_nameoffset; uint32_t rm_namelen; uint32_t rm_type; uint32_t rm_valueoffset; uint32_t rm_valuelen; }; /* Perform a soft reset on the device. */ #define REMOTE_NDIS_RESET_MSG 0x00000006 #define REMOTE_NDIS_RESET_CMPLT 0x80000006 struct rndis_reset_req { uint32_t rm_type; uint32_t rm_len; uint32_t rm_rid; }; struct rndis_reset_comp { uint32_t rm_type; uint32_t rm_len; uint32_t rm_status; uint32_t rm_adrreset; }; -/* 802.3 link-state or undefined message error. */ +/* 802.3 link-state or undefined message error. Sent by device. */ #define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007 + +struct rndis_status_msg { + uint32_t rm_type; + uint32_t rm_len; + uint32_t rm_status; + uint32_t rm_stbuflen; + uint32_t rm_stbufoffset; + /* rndis_diag_info */ +}; + +/* + * Immediately after rndis_status_msg.rm_stbufoffset, if a control + * message is malformatted, or a packet message contains inappropriate + * content. + */ +struct rndis_diag_info { + uint32_t rm_diagstatus; + uint32_t rm_erroffset; +}; /* Keepalive messsage. May be sent by device. */ #define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008 #define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008 struct rndis_keepalive_req { uint32_t rm_type; uint32_t rm_len; uint32_t rm_rid; }; struct rndis_keepalive_comp { uint32_t rm_type; uint32_t rm_len; uint32_t rm_rid; uint32_t rm_status; }; /* packet filter bits used by OID_GEN_CURRENT_PACKET_FILTER */ #define NDIS_PACKET_TYPE_DIRECTED 0x00000001 #define NDIS_PACKET_TYPE_MULTICAST 0x00000002 #define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 #define NDIS_PACKET_TYPE_BROADCAST 0x00000008 #define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 #define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 #define NDIS_PACKET_TYPE_SMT 0x00000040 #define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 #define NDIS_PACKET_TYPE_GROUP 0x00001000 #define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00002000 #define NDIS_PACKET_TYPE_FUNCTIONAL 0x00004000 #define NDIS_PACKET_TYPE_MAC_FRAME 0x00008000 /* RNDIS offsets */ #define RNDIS_HEADER_OFFSET ((uint32_t)sizeof(struct rndis_msghdr)) #define RNDIS_DATA_OFFSET \ ((uint32_t)(sizeof(struct rndis_packet_msg) - RNDIS_HEADER_OFFSET)) #endif /* !_NET_RNDIS_H_ */ Index: projects/clang390-import/sys/sparc64/sparc64/pmap.c =================================================================== --- projects/clang390-import/sys/sparc64/sparc64/pmap.c (revision 305430) +++ projects/clang390-import/sys/sparc64/sparc64/pmap.c (revision 305431) @@ -1,2321 +1,2334 @@ /*- * Copyright (c) 1991 Regents of the University of California. * All rights reserved. * Copyright (c) 1994 John S. Dyson * All rights reserved. * Copyright (c) 1994 David Greenman * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department and William Jolitz of UUNET Technologies Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)pmap.c 7.7 (Berkeley) 5/12/91 */ #include __FBSDID("$FreeBSD$"); /* * Manages physical address maps. * * Since the information managed by this module is also stored by the * logical address mapping module, this module may throw away valid virtual * to physical mappings at almost any time. However, invalidations of * mappings must be done as requested. * * In order to cope with hardware architectures which make virtual to * physical map invalidates expensive, this module may delay invalidate * reduced protection operations until such time as they are actually * necessary. This module is given full information as to which processors * are currently using which maps, and to when physical maps must be made * correct. */ #include "opt_kstack_pages.h" #include "opt_pmap.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Virtual address of message buffer */ struct msgbuf *msgbufp; /* * Map of physical memory reagions */ vm_paddr_t phys_avail[128]; static struct ofw_mem_region mra[128]; struct ofw_mem_region sparc64_memreg[128]; int sparc64_nmemreg; static struct ofw_map translations[128]; static int translations_size; static vm_offset_t pmap_idle_map; static vm_offset_t pmap_temp_map_1; static vm_offset_t pmap_temp_map_2; /* * First and last available kernel virtual addresses */ vm_offset_t virtual_avail; vm_offset_t virtual_end; vm_offset_t kernel_vm_end; vm_offset_t vm_max_kernel_address; /* * Kernel pmap */ struct pmap kernel_pmap_store; struct rwlock_padalign tte_list_global_lock; /* * Allocate physical memory for use in pmap_bootstrap. */ static vm_paddr_t pmap_bootstrap_alloc(vm_size_t size, uint32_t colors); static void pmap_bootstrap_set_tte(struct tte *tp, u_long vpn, u_long data); static void pmap_cache_remove(vm_page_t m, vm_offset_t va); static int pmap_protect_tte(struct pmap *pm1, struct pmap *pm2, struct tte *tp, vm_offset_t va); static int pmap_unwire_tte(pmap_t pm, pmap_t pm2, struct tte *tp, vm_offset_t va); static void pmap_init_qpages(void); /* * Map the given physical page at the specified virtual address in the * target pmap with the protection requested. If specified the page * will be wired down. * * The page queues and pmap must be locked. */ static int pmap_enter_locked(pmap_t pm, vm_offset_t va, vm_page_t m, vm_prot_t prot, u_int flags, int8_t psind); extern int tl1_dmmu_miss_direct_patch_tsb_phys_1[]; extern int tl1_dmmu_miss_direct_patch_tsb_phys_end_1[]; extern int tl1_dmmu_miss_patch_asi_1[]; extern int tl1_dmmu_miss_patch_quad_ldd_1[]; extern int tl1_dmmu_miss_patch_tsb_1[]; extern int tl1_dmmu_miss_patch_tsb_2[]; extern int tl1_dmmu_miss_patch_tsb_mask_1[]; extern int tl1_dmmu_miss_patch_tsb_mask_2[]; extern int tl1_dmmu_prot_patch_asi_1[]; extern int tl1_dmmu_prot_patch_quad_ldd_1[]; extern int tl1_dmmu_prot_patch_tsb_1[]; extern int tl1_dmmu_prot_patch_tsb_2[]; extern int tl1_dmmu_prot_patch_tsb_mask_1[]; extern int tl1_dmmu_prot_patch_tsb_mask_2[]; extern int tl1_immu_miss_patch_asi_1[]; extern int tl1_immu_miss_patch_quad_ldd_1[]; extern int tl1_immu_miss_patch_tsb_1[]; extern int tl1_immu_miss_patch_tsb_2[]; extern int tl1_immu_miss_patch_tsb_mask_1[]; extern int tl1_immu_miss_patch_tsb_mask_2[]; /* * If user pmap is processed with pmap_remove and with pmap_remove and the * resident count drops to 0, there are no more pages to remove, so we * need not continue. */ #define PMAP_REMOVE_DONE(pm) \ ((pm) != kernel_pmap && (pm)->pm_stats.resident_count == 0) /* * The threshold (in bytes) above which tsb_foreach() is used in pmap_remove() * and pmap_protect() instead of trying each virtual address. */ #define PMAP_TSB_THRESH ((TSB_SIZE / 2) * PAGE_SIZE) SYSCTL_NODE(_debug, OID_AUTO, pmap_stats, CTLFLAG_RD, 0, ""); PMAP_STATS_VAR(pmap_nenter); PMAP_STATS_VAR(pmap_nenter_update); PMAP_STATS_VAR(pmap_nenter_replace); PMAP_STATS_VAR(pmap_nenter_new); PMAP_STATS_VAR(pmap_nkenter); PMAP_STATS_VAR(pmap_nkenter_oc); PMAP_STATS_VAR(pmap_nkenter_stupid); PMAP_STATS_VAR(pmap_nkremove); PMAP_STATS_VAR(pmap_nqenter); PMAP_STATS_VAR(pmap_nqremove); PMAP_STATS_VAR(pmap_ncache_enter); PMAP_STATS_VAR(pmap_ncache_enter_c); PMAP_STATS_VAR(pmap_ncache_enter_oc); PMAP_STATS_VAR(pmap_ncache_enter_cc); PMAP_STATS_VAR(pmap_ncache_enter_coc); PMAP_STATS_VAR(pmap_ncache_enter_nc); PMAP_STATS_VAR(pmap_ncache_enter_cnc); PMAP_STATS_VAR(pmap_ncache_remove); PMAP_STATS_VAR(pmap_ncache_remove_c); PMAP_STATS_VAR(pmap_ncache_remove_oc); PMAP_STATS_VAR(pmap_ncache_remove_cc); PMAP_STATS_VAR(pmap_ncache_remove_coc); PMAP_STATS_VAR(pmap_ncache_remove_nc); PMAP_STATS_VAR(pmap_nzero_page); PMAP_STATS_VAR(pmap_nzero_page_c); PMAP_STATS_VAR(pmap_nzero_page_oc); PMAP_STATS_VAR(pmap_nzero_page_nc); PMAP_STATS_VAR(pmap_nzero_page_area); PMAP_STATS_VAR(pmap_nzero_page_area_c); PMAP_STATS_VAR(pmap_nzero_page_area_oc); PMAP_STATS_VAR(pmap_nzero_page_area_nc); PMAP_STATS_VAR(pmap_ncopy_page); PMAP_STATS_VAR(pmap_ncopy_page_c); PMAP_STATS_VAR(pmap_ncopy_page_oc); PMAP_STATS_VAR(pmap_ncopy_page_nc); PMAP_STATS_VAR(pmap_ncopy_page_dc); PMAP_STATS_VAR(pmap_ncopy_page_doc); PMAP_STATS_VAR(pmap_ncopy_page_sc); PMAP_STATS_VAR(pmap_ncopy_page_soc); PMAP_STATS_VAR(pmap_nnew_thread); PMAP_STATS_VAR(pmap_nnew_thread_oc); static inline u_long dtlb_get_data(u_int tlb, u_int slot); /* * Quick sort callout for comparing memory regions */ static int mr_cmp(const void *a, const void *b); static int om_cmp(const void *a, const void *b); static int mr_cmp(const void *a, const void *b) { const struct ofw_mem_region *mra; const struct ofw_mem_region *mrb; mra = a; mrb = b; if (mra->mr_start < mrb->mr_start) return (-1); else if (mra->mr_start > mrb->mr_start) return (1); else return (0); } static int om_cmp(const void *a, const void *b) { const struct ofw_map *oma; const struct ofw_map *omb; oma = a; omb = b; if (oma->om_start < omb->om_start) return (-1); else if (oma->om_start > omb->om_start) return (1); else return (0); } static inline u_long dtlb_get_data(u_int tlb, u_int slot) { u_long data; register_t s; slot = TLB_DAR_SLOT(tlb, slot); /* * We read ASI_DTLB_DATA_ACCESS_REG twice back-to-back in order to * work around errata of USIII and beyond. */ s = intr_disable(); (void)ldxa(slot, ASI_DTLB_DATA_ACCESS_REG); data = ldxa(slot, ASI_DTLB_DATA_ACCESS_REG); intr_restore(s); return (data); } /* * Bootstrap the system enough to run with virtual memory. */ void pmap_bootstrap(u_int cpu_impl) { struct pmap *pm; struct tte *tp; vm_offset_t off; vm_offset_t va; vm_paddr_t pa; vm_size_t physsz; vm_size_t virtsz; u_long data; u_long vpn; phandle_t pmem; phandle_t vmem; u_int dtlb_slots_avail; int i; int j; int sz; uint32_t asi; uint32_t colors; uint32_t ldd; /* * Set the kernel context. */ pmap_set_kctx(); colors = dcache_color_ignore != 0 ? 1 : DCACHE_COLORS; /* * Find out what physical memory is available from the PROM and * initialize the phys_avail array. This must be done before * pmap_bootstrap_alloc is called. */ if ((pmem = OF_finddevice("/memory")) == -1) OF_panic("%s: finddevice /memory", __func__); if ((sz = OF_getproplen(pmem, "available")) == -1) OF_panic("%s: getproplen /memory/available", __func__); if (sizeof(phys_avail) < sz) OF_panic("%s: phys_avail too small", __func__); if (sizeof(mra) < sz) OF_panic("%s: mra too small", __func__); bzero(mra, sz); if (OF_getprop(pmem, "available", mra, sz) == -1) OF_panic("%s: getprop /memory/available", __func__); sz /= sizeof(*mra); #ifdef DIAGNOSTIC OF_printf("pmap_bootstrap: physical memory\n"); #endif qsort(mra, sz, sizeof (*mra), mr_cmp); physsz = 0; getenv_quad("hw.physmem", &physmem); physmem = btoc(physmem); for (i = 0, j = 0; i < sz; i++, j += 2) { #ifdef DIAGNOSTIC OF_printf("start=%#lx size=%#lx\n", mra[i].mr_start, mra[i].mr_size); #endif if (physmem != 0 && btoc(physsz + mra[i].mr_size) >= physmem) { if (btoc(physsz) < physmem) { phys_avail[j] = mra[i].mr_start; phys_avail[j + 1] = mra[i].mr_start + (ctob(physmem) - physsz); physsz = ctob(physmem); } break; } phys_avail[j] = mra[i].mr_start; phys_avail[j + 1] = mra[i].mr_start + mra[i].mr_size; physsz += mra[i].mr_size; } physmem = btoc(physsz); /* * Calculate the size of kernel virtual memory, and the size and mask * for the kernel TSB based on the phsyical memory size but limited * by the amount of dTLB slots available for locked entries if we have * to lock the TSB in the TLB (given that for spitfire-class CPUs all * of the dt64 slots can hold locked entries but there is no large * dTLB for unlocked ones, we don't use more than half of it for the * TSB). * Note that for reasons unknown OpenSolaris doesn't take advantage of * ASI_ATOMIC_QUAD_LDD_PHYS on UltraSPARC-III. However, given that no * public documentation is available for these, the latter just might * not support it, yet. */ if (cpu_impl == CPU_IMPL_SPARC64V || cpu_impl >= CPU_IMPL_ULTRASPARCIIIp) { tsb_kernel_ldd_phys = 1; virtsz = roundup(5 / 3 * physsz, PAGE_SIZE_4M << (PAGE_SHIFT - TTE_SHIFT)); } else { dtlb_slots_avail = 0; for (i = 0; i < dtlb_slots; i++) { data = dtlb_get_data(cpu_impl == CPU_IMPL_ULTRASPARCIII ? TLB_DAR_T16 : TLB_DAR_T32, i); if ((data & (TD_V | TD_L)) != (TD_V | TD_L)) dtlb_slots_avail++; } #ifdef SMP dtlb_slots_avail -= PCPU_PAGES; #endif if (cpu_impl >= CPU_IMPL_ULTRASPARCI && cpu_impl < CPU_IMPL_ULTRASPARCIII) dtlb_slots_avail /= 2; virtsz = roundup(physsz, PAGE_SIZE_4M << (PAGE_SHIFT - TTE_SHIFT)); virtsz = MIN(virtsz, (dtlb_slots_avail * PAGE_SIZE_4M) << (PAGE_SHIFT - TTE_SHIFT)); } vm_max_kernel_address = VM_MIN_KERNEL_ADDRESS + virtsz; tsb_kernel_size = virtsz >> (PAGE_SHIFT - TTE_SHIFT); tsb_kernel_mask = (tsb_kernel_size >> TTE_SHIFT) - 1; /* * Allocate the kernel TSB and lock it in the TLB if necessary. */ pa = pmap_bootstrap_alloc(tsb_kernel_size, colors); if (pa & PAGE_MASK_4M) OF_panic("%s: TSB unaligned", __func__); tsb_kernel_phys = pa; if (tsb_kernel_ldd_phys == 0) { tsb_kernel = (struct tte *)(VM_MIN_KERNEL_ADDRESS - tsb_kernel_size); pmap_map_tsb(); bzero(tsb_kernel, tsb_kernel_size); } else { tsb_kernel = (struct tte *)TLB_PHYS_TO_DIRECT(tsb_kernel_phys); aszero(ASI_PHYS_USE_EC, tsb_kernel_phys, tsb_kernel_size); } /* * Allocate and map the dynamic per-CPU area for the BSP. */ pa = pmap_bootstrap_alloc(DPCPU_SIZE, colors); dpcpu0 = (void *)TLB_PHYS_TO_DIRECT(pa); /* * Allocate and map the message buffer. */ pa = pmap_bootstrap_alloc(msgbufsize, colors); msgbufp = (struct msgbuf *)TLB_PHYS_TO_DIRECT(pa); /* * Patch the TSB addresses and mask as well as the ASIs used to load * it into the trap table. */ #define LDDA_R_I_R(rd, imm_asi, rs1, rs2) \ (EIF_OP(IOP_LDST) | EIF_F3_RD(rd) | EIF_F3_OP3(INS3_LDDA) | \ EIF_F3_RS1(rs1) | EIF_F3_I(0) | EIF_F3_IMM_ASI(imm_asi) | \ EIF_F3_RS2(rs2)) #define OR_R_I_R(rd, imm13, rs1) \ (EIF_OP(IOP_MISC) | EIF_F3_RD(rd) | EIF_F3_OP3(INS2_OR) | \ EIF_F3_RS1(rs1) | EIF_F3_I(1) | EIF_IMM(imm13, 13)) #define SETHI(rd, imm22) \ (EIF_OP(IOP_FORM2) | EIF_F2_RD(rd) | EIF_F2_OP2(INS0_SETHI) | \ EIF_IMM((imm22) >> 10, 22)) #define WR_R_I(rd, imm13, rs1) \ (EIF_OP(IOP_MISC) | EIF_F3_RD(rd) | EIF_F3_OP3(INS2_WR) | \ EIF_F3_RS1(rs1) | EIF_F3_I(1) | EIF_IMM(imm13, 13)) #define PATCH_ASI(addr, asi) do { \ if (addr[0] != WR_R_I(IF_F3_RD(addr[0]), 0x0, \ IF_F3_RS1(addr[0]))) \ OF_panic("%s: patched instructions have changed", \ __func__); \ addr[0] |= EIF_IMM((asi), 13); \ flush(addr); \ } while (0) #define PATCH_LDD(addr, asi) do { \ if (addr[0] != LDDA_R_I_R(IF_F3_RD(addr[0]), 0x0, \ IF_F3_RS1(addr[0]), IF_F3_RS2(addr[0]))) \ OF_panic("%s: patched instructions have changed", \ __func__); \ addr[0] |= EIF_F3_IMM_ASI(asi); \ flush(addr); \ } while (0) #define PATCH_TSB(addr, val) do { \ if (addr[0] != SETHI(IF_F2_RD(addr[0]), 0x0) || \ addr[1] != OR_R_I_R(IF_F3_RD(addr[1]), 0x0, \ IF_F3_RS1(addr[1])) || \ addr[3] != SETHI(IF_F2_RD(addr[3]), 0x0)) \ OF_panic("%s: patched instructions have changed", \ __func__); \ addr[0] |= EIF_IMM((val) >> 42, 22); \ addr[1] |= EIF_IMM((val) >> 32, 10); \ addr[3] |= EIF_IMM((val) >> 10, 22); \ flush(addr); \ flush(addr + 1); \ flush(addr + 3); \ } while (0) #define PATCH_TSB_MASK(addr, val) do { \ if (addr[0] != SETHI(IF_F2_RD(addr[0]), 0x0) || \ addr[1] != OR_R_I_R(IF_F3_RD(addr[1]), 0x0, \ IF_F3_RS1(addr[1]))) \ OF_panic("%s: patched instructions have changed", \ __func__); \ addr[0] |= EIF_IMM((val) >> 10, 22); \ addr[1] |= EIF_IMM((val), 10); \ flush(addr); \ flush(addr + 1); \ } while (0) if (tsb_kernel_ldd_phys == 0) { asi = ASI_N; ldd = ASI_NUCLEUS_QUAD_LDD; off = (vm_offset_t)tsb_kernel; } else { asi = ASI_PHYS_USE_EC; ldd = ASI_ATOMIC_QUAD_LDD_PHYS; off = (vm_offset_t)tsb_kernel_phys; } PATCH_TSB(tl1_dmmu_miss_direct_patch_tsb_phys_1, tsb_kernel_phys); PATCH_TSB(tl1_dmmu_miss_direct_patch_tsb_phys_end_1, tsb_kernel_phys + tsb_kernel_size - 1); PATCH_ASI(tl1_dmmu_miss_patch_asi_1, asi); PATCH_LDD(tl1_dmmu_miss_patch_quad_ldd_1, ldd); PATCH_TSB(tl1_dmmu_miss_patch_tsb_1, off); PATCH_TSB(tl1_dmmu_miss_patch_tsb_2, off); PATCH_TSB_MASK(tl1_dmmu_miss_patch_tsb_mask_1, tsb_kernel_mask); PATCH_TSB_MASK(tl1_dmmu_miss_patch_tsb_mask_2, tsb_kernel_mask); PATCH_ASI(tl1_dmmu_prot_patch_asi_1, asi); PATCH_LDD(tl1_dmmu_prot_patch_quad_ldd_1, ldd); PATCH_TSB(tl1_dmmu_prot_patch_tsb_1, off); PATCH_TSB(tl1_dmmu_prot_patch_tsb_2, off); PATCH_TSB_MASK(tl1_dmmu_prot_patch_tsb_mask_1, tsb_kernel_mask); PATCH_TSB_MASK(tl1_dmmu_prot_patch_tsb_mask_2, tsb_kernel_mask); PATCH_ASI(tl1_immu_miss_patch_asi_1, asi); PATCH_LDD(tl1_immu_miss_patch_quad_ldd_1, ldd); PATCH_TSB(tl1_immu_miss_patch_tsb_1, off); PATCH_TSB(tl1_immu_miss_patch_tsb_2, off); PATCH_TSB_MASK(tl1_immu_miss_patch_tsb_mask_1, tsb_kernel_mask); PATCH_TSB_MASK(tl1_immu_miss_patch_tsb_mask_2, tsb_kernel_mask); /* * Enter fake 8k pages for the 4MB kernel pages, so that * pmap_kextract() will work for them. */ for (i = 0; i < kernel_tlb_slots; i++) { pa = kernel_tlbs[i].te_pa; va = kernel_tlbs[i].te_va; for (off = 0; off < PAGE_SIZE_4M; off += PAGE_SIZE) { tp = tsb_kvtotte(va + off); vpn = TV_VPN(va + off, TS_8K); data = TD_V | TD_8K | TD_PA(pa + off) | TD_REF | TD_SW | TD_CP | TD_CV | TD_P | TD_W; pmap_bootstrap_set_tte(tp, vpn, data); } } /* * Set the start and end of KVA. The kernel is loaded starting * at the first available 4MB super page, so we advance to the * end of the last one used for it. */ virtual_avail = KERNBASE + kernel_tlb_slots * PAGE_SIZE_4M; virtual_end = vm_max_kernel_address; kernel_vm_end = vm_max_kernel_address; /* * Allocate kva space for temporary mappings. */ pmap_idle_map = virtual_avail; virtual_avail += PAGE_SIZE * colors; pmap_temp_map_1 = virtual_avail; virtual_avail += PAGE_SIZE * colors; pmap_temp_map_2 = virtual_avail; virtual_avail += PAGE_SIZE * colors; /* * Allocate a kernel stack with guard page for thread0 and map it * into the kernel TSB. We must ensure that the virtual address is * colored properly for corresponding CPUs, since we're allocating * from phys_avail so the memory won't have an associated vm_page_t. */ pa = pmap_bootstrap_alloc(KSTACK_PAGES * PAGE_SIZE, colors); kstack0_phys = pa; virtual_avail += roundup(KSTACK_GUARD_PAGES, colors) * PAGE_SIZE; kstack0 = virtual_avail; virtual_avail += roundup(KSTACK_PAGES, colors) * PAGE_SIZE; if (dcache_color_ignore == 0) KASSERT(DCACHE_COLOR(kstack0) == DCACHE_COLOR(kstack0_phys), ("pmap_bootstrap: kstack0 miscolored")); for (i = 0; i < KSTACK_PAGES; i++) { pa = kstack0_phys + i * PAGE_SIZE; va = kstack0 + i * PAGE_SIZE; tp = tsb_kvtotte(va); vpn = TV_VPN(va, TS_8K); data = TD_V | TD_8K | TD_PA(pa) | TD_REF | TD_SW | TD_CP | TD_CV | TD_P | TD_W; pmap_bootstrap_set_tte(tp, vpn, data); } /* * Calculate the last available physical address. */ for (i = 0; phys_avail[i + 2] != 0; i += 2) ; Maxmem = sparc64_btop(phys_avail[i + 1]); /* * Add the PROM mappings to the kernel TSB. */ if ((vmem = OF_finddevice("/virtual-memory")) == -1) OF_panic("%s: finddevice /virtual-memory", __func__); if ((sz = OF_getproplen(vmem, "translations")) == -1) OF_panic("%s: getproplen translations", __func__); if (sizeof(translations) < sz) OF_panic("%s: translations too small", __func__); bzero(translations, sz); if (OF_getprop(vmem, "translations", translations, sz) == -1) OF_panic("%s: getprop /virtual-memory/translations", __func__); sz /= sizeof(*translations); translations_size = sz; #ifdef DIAGNOSTIC OF_printf("pmap_bootstrap: translations\n"); #endif qsort(translations, sz, sizeof (*translations), om_cmp); for (i = 0; i < sz; i++) { #ifdef DIAGNOSTIC OF_printf("translation: start=%#lx size=%#lx tte=%#lx\n", translations[i].om_start, translations[i].om_size, translations[i].om_tte); #endif if ((translations[i].om_tte & TD_V) == 0) continue; if (translations[i].om_start < VM_MIN_PROM_ADDRESS || translations[i].om_start > VM_MAX_PROM_ADDRESS) continue; for (off = 0; off < translations[i].om_size; off += PAGE_SIZE) { va = translations[i].om_start + off; tp = tsb_kvtotte(va); vpn = TV_VPN(va, TS_8K); data = ((translations[i].om_tte & ~((TD_SOFT2_MASK << TD_SOFT2_SHIFT) | (cpu_impl >= CPU_IMPL_ULTRASPARCI && cpu_impl < CPU_IMPL_ULTRASPARCIII ? (TD_DIAG_SF_MASK << TD_DIAG_SF_SHIFT) : (TD_RSVD_CH_MASK << TD_RSVD_CH_SHIFT)) | (TD_SOFT_MASK << TD_SOFT_SHIFT))) | TD_EXEC) + off; pmap_bootstrap_set_tte(tp, vpn, data); } } /* * Get the available physical memory ranges from /memory/reg. These * are only used for kernel dumps, but it may not be wise to do PROM * calls in that situation. */ if ((sz = OF_getproplen(pmem, "reg")) == -1) OF_panic("%s: getproplen /memory/reg", __func__); if (sizeof(sparc64_memreg) < sz) OF_panic("%s: sparc64_memreg too small", __func__); if (OF_getprop(pmem, "reg", sparc64_memreg, sz) == -1) OF_panic("%s: getprop /memory/reg", __func__); sparc64_nmemreg = sz / sizeof(*sparc64_memreg); /* * Initialize the kernel pmap (which is statically allocated). */ pm = kernel_pmap; PMAP_LOCK_INIT(pm); for (i = 0; i < MAXCPU; i++) pm->pm_context[i] = TLB_CTX_KERNEL; CPU_FILL(&pm->pm_active); /* * Initialize the global tte list lock, which is more commonly * known as the pmap pv global lock. */ rw_init(&tte_list_global_lock, "pmap pv global"); /* * Flush all non-locked TLB entries possibly left over by the * firmware. */ tlb_flush_nonlocked(); } static void pmap_init_qpages(void) { struct pcpu *pc; int i; if (dcache_color_ignore != 0) return; CPU_FOREACH(i) { pc = pcpu_find(i); pc->pc_qmap_addr = kva_alloc(PAGE_SIZE * DCACHE_COLORS); if (pc->pc_qmap_addr == 0) panic("pmap_init_qpages: unable to allocate KVA"); } } SYSINIT(qpages_init, SI_SUB_CPU, SI_ORDER_ANY, pmap_init_qpages, NULL); /* * Map the 4MB kernel TSB pages. */ void pmap_map_tsb(void) { vm_offset_t va; vm_paddr_t pa; u_long data; int i; for (i = 0; i < tsb_kernel_size; i += PAGE_SIZE_4M) { va = (vm_offset_t)tsb_kernel + i; pa = tsb_kernel_phys + i; data = TD_V | TD_4M | TD_PA(pa) | TD_L | TD_CP | TD_CV | TD_P | TD_W; stxa(AA_DMMU_TAR, ASI_DMMU, TLB_TAR_VA(va) | TLB_TAR_CTX(TLB_CTX_KERNEL)); stxa_sync(0, ASI_DTLB_DATA_IN_REG, data); } } /* * Set the secondary context to be the kernel context (needed for FP block * operations in the kernel). */ void pmap_set_kctx(void) { stxa(AA_DMMU_SCXR, ASI_DMMU, (ldxa(AA_DMMU_SCXR, ASI_DMMU) & TLB_CXR_PGSZ_MASK) | TLB_CTX_KERNEL); flush(KERNBASE); } /* * Allocate a physical page of memory directly from the phys_avail map. * Can only be called from pmap_bootstrap before avail start and end are * calculated. */ static vm_paddr_t pmap_bootstrap_alloc(vm_size_t size, uint32_t colors) { vm_paddr_t pa; int i; size = roundup(size, PAGE_SIZE * colors); for (i = 0; phys_avail[i + 1] != 0; i += 2) { if (phys_avail[i + 1] - phys_avail[i] < size) continue; pa = phys_avail[i]; phys_avail[i] += size; return (pa); } OF_panic("%s: no suitable region found", __func__); } /* * Set a TTE. This function is intended as a helper when tsb_kernel is * direct-mapped but we haven't taken over the trap table, yet, as it's the * case when we are taking advantage of ASI_ATOMIC_QUAD_LDD_PHYS to access * the kernel TSB. */ void pmap_bootstrap_set_tte(struct tte *tp, u_long vpn, u_long data) { if (tsb_kernel_ldd_phys == 0) { tp->tte_vpn = vpn; tp->tte_data = data; } else { stxa((vm_paddr_t)tp + offsetof(struct tte, tte_vpn), ASI_PHYS_USE_EC, vpn); stxa((vm_paddr_t)tp + offsetof(struct tte, tte_data), ASI_PHYS_USE_EC, data); } } /* * Initialize a vm_page's machine-dependent fields. */ void pmap_page_init(vm_page_t m) { TAILQ_INIT(&m->md.tte_list); m->md.color = DCACHE_COLOR(VM_PAGE_TO_PHYS(m)); m->md.pmap = NULL; } /* * Initialize the pmap module. */ void pmap_init(void) { vm_offset_t addr; vm_size_t size; int result; int i; for (i = 0; i < translations_size; i++) { addr = translations[i].om_start; size = translations[i].om_size; if ((translations[i].om_tte & TD_V) == 0) continue; if (addr < VM_MIN_PROM_ADDRESS || addr > VM_MAX_PROM_ADDRESS) continue; result = vm_map_find(kernel_map, NULL, 0, &addr, size, 0, VMFS_NO_SPACE, VM_PROT_ALL, VM_PROT_ALL, MAP_NOFAULT); if (result != KERN_SUCCESS || addr != translations[i].om_start) panic("pmap_init: vm_map_find"); } } /* * Extract the physical page address associated with the given * map/virtual_address pair. */ vm_paddr_t pmap_extract(pmap_t pm, vm_offset_t va) { struct tte *tp; vm_paddr_t pa; if (pm == kernel_pmap) return (pmap_kextract(va)); PMAP_LOCK(pm); tp = tsb_tte_lookup(pm, va); if (tp == NULL) pa = 0; else pa = TTE_GET_PA(tp) | (va & TTE_GET_PAGE_MASK(tp)); PMAP_UNLOCK(pm); return (pa); } /* * Atomically extract and hold the physical page with the given * pmap and virtual address pair if that mapping permits the given * protection. */ vm_page_t pmap_extract_and_hold(pmap_t pm, vm_offset_t va, vm_prot_t prot) { struct tte *tp; vm_page_t m; vm_paddr_t pa; m = NULL; pa = 0; PMAP_LOCK(pm); retry: if (pm == kernel_pmap) { if (va >= VM_MIN_DIRECT_ADDRESS) { tp = NULL; m = PHYS_TO_VM_PAGE(TLB_DIRECT_TO_PHYS(va)); (void)vm_page_pa_tryrelock(pm, TLB_DIRECT_TO_PHYS(va), &pa); vm_page_hold(m); } else { tp = tsb_kvtotte(va); if ((tp->tte_data & TD_V) == 0) tp = NULL; } } else tp = tsb_tte_lookup(pm, va); if (tp != NULL && ((tp->tte_data & TD_SW) || (prot & VM_PROT_WRITE) == 0)) { if (vm_page_pa_tryrelock(pm, TTE_GET_PA(tp), &pa)) goto retry; m = PHYS_TO_VM_PAGE(TTE_GET_PA(tp)); vm_page_hold(m); } PA_UNLOCK_COND(pa); PMAP_UNLOCK(pm); return (m); } /* * Extract the physical page address associated with the given kernel virtual * address. */ vm_paddr_t pmap_kextract(vm_offset_t va) { struct tte *tp; if (va >= VM_MIN_DIRECT_ADDRESS) return (TLB_DIRECT_TO_PHYS(va)); tp = tsb_kvtotte(va); if ((tp->tte_data & TD_V) == 0) return (0); return (TTE_GET_PA(tp) | (va & TTE_GET_PAGE_MASK(tp))); } int pmap_cache_enter(vm_page_t m, vm_offset_t va) { struct tte *tp; int color; rw_assert(&tte_list_global_lock, RA_WLOCKED); KASSERT((m->flags & PG_FICTITIOUS) == 0, ("pmap_cache_enter: fake page")); PMAP_STATS_INC(pmap_ncache_enter); if (dcache_color_ignore != 0) return (1); /* * Find the color for this virtual address and note the added mapping. */ color = DCACHE_COLOR(va); m->md.colors[color]++; /* * If all existing mappings have the same color, the mapping is * cacheable. */ if (m->md.color == color) { KASSERT(m->md.colors[DCACHE_OTHER_COLOR(color)] == 0, ("pmap_cache_enter: cacheable, mappings of other color")); if (m->md.color == DCACHE_COLOR(VM_PAGE_TO_PHYS(m))) PMAP_STATS_INC(pmap_ncache_enter_c); else PMAP_STATS_INC(pmap_ncache_enter_oc); return (1); } /* * If there are no mappings of the other color, and the page still has * the wrong color, this must be a new mapping. Change the color to * match the new mapping, which is cacheable. We must flush the page * from the cache now. */ if (m->md.colors[DCACHE_OTHER_COLOR(color)] == 0) { KASSERT(m->md.colors[color] == 1, ("pmap_cache_enter: changing color, not new mapping")); dcache_page_inval(VM_PAGE_TO_PHYS(m)); m->md.color = color; if (m->md.color == DCACHE_COLOR(VM_PAGE_TO_PHYS(m))) PMAP_STATS_INC(pmap_ncache_enter_cc); else PMAP_STATS_INC(pmap_ncache_enter_coc); return (1); } /* * If the mapping is already non-cacheable, just return. */ if (m->md.color == -1) { PMAP_STATS_INC(pmap_ncache_enter_nc); return (0); } PMAP_STATS_INC(pmap_ncache_enter_cnc); /* * Mark all mappings as uncacheable, flush any lines with the other * color out of the dcache, and set the color to none (-1). */ TAILQ_FOREACH(tp, &m->md.tte_list, tte_link) { atomic_clear_long(&tp->tte_data, TD_CV); tlb_page_demap(TTE_GET_PMAP(tp), TTE_GET_VA(tp)); } dcache_page_inval(VM_PAGE_TO_PHYS(m)); m->md.color = -1; return (0); } static void pmap_cache_remove(vm_page_t m, vm_offset_t va) { struct tte *tp; int color; rw_assert(&tte_list_global_lock, RA_WLOCKED); CTR3(KTR_PMAP, "pmap_cache_remove: m=%p va=%#lx c=%d", m, va, m->md.colors[DCACHE_COLOR(va)]); KASSERT((m->flags & PG_FICTITIOUS) == 0, ("pmap_cache_remove: fake page")); PMAP_STATS_INC(pmap_ncache_remove); if (dcache_color_ignore != 0) return; KASSERT(m->md.colors[DCACHE_COLOR(va)] > 0, ("pmap_cache_remove: no mappings %d <= 0", m->md.colors[DCACHE_COLOR(va)])); /* * Find the color for this virtual address and note the removal of * the mapping. */ color = DCACHE_COLOR(va); m->md.colors[color]--; /* * If the page is cacheable, just return and keep the same color, even * if there are no longer any mappings. */ if (m->md.color != -1) { if (m->md.color == DCACHE_COLOR(VM_PAGE_TO_PHYS(m))) PMAP_STATS_INC(pmap_ncache_remove_c); else PMAP_STATS_INC(pmap_ncache_remove_oc); return; } KASSERT(m->md.colors[DCACHE_OTHER_COLOR(color)] != 0, ("pmap_cache_remove: uncacheable, no mappings of other color")); /* * If the page is not cacheable (color is -1), and the number of * mappings for this color is not zero, just return. There are * mappings of the other color still, so remain non-cacheable. */ if (m->md.colors[color] != 0) { PMAP_STATS_INC(pmap_ncache_remove_nc); return; } /* * The number of mappings for this color is now zero. Recache the * other colored mappings, and change the page color to the other * color. There should be no lines in the data cache for this page, * so flushing should not be needed. */ TAILQ_FOREACH(tp, &m->md.tte_list, tte_link) { atomic_set_long(&tp->tte_data, TD_CV); tlb_page_demap(TTE_GET_PMAP(tp), TTE_GET_VA(tp)); } m->md.color = DCACHE_OTHER_COLOR(color); if (m->md.color == DCACHE_COLOR(VM_PAGE_TO_PHYS(m))) PMAP_STATS_INC(pmap_ncache_remove_cc); else PMAP_STATS_INC(pmap_ncache_remove_coc); } /* * Map a wired page into kernel virtual address space. */ void pmap_kenter(vm_offset_t va, vm_page_t m) { vm_offset_t ova; struct tte *tp; vm_page_t om; u_long data; rw_assert(&tte_list_global_lock, RA_WLOCKED); PMAP_STATS_INC(pmap_nkenter); tp = tsb_kvtotte(va); CTR4(KTR_PMAP, "pmap_kenter: va=%#lx pa=%#lx tp=%p data=%#lx", va, VM_PAGE_TO_PHYS(m), tp, tp->tte_data); if (DCACHE_COLOR(VM_PAGE_TO_PHYS(m)) != DCACHE_COLOR(va)) { CTR5(KTR_SPARE2, "pmap_kenter: off color va=%#lx pa=%#lx o=%p ot=%d pi=%#lx", va, VM_PAGE_TO_PHYS(m), m->object, m->object ? m->object->type : -1, m->pindex); PMAP_STATS_INC(pmap_nkenter_oc); } if ((tp->tte_data & TD_V) != 0) { om = PHYS_TO_VM_PAGE(TTE_GET_PA(tp)); ova = TTE_GET_VA(tp); if (m == om && va == ova) { PMAP_STATS_INC(pmap_nkenter_stupid); return; } TAILQ_REMOVE(&om->md.tte_list, tp, tte_link); pmap_cache_remove(om, ova); if (va != ova) tlb_page_demap(kernel_pmap, ova); } data = TD_V | TD_8K | VM_PAGE_TO_PHYS(m) | TD_REF | TD_SW | TD_CP | TD_P | TD_W; if (pmap_cache_enter(m, va) != 0) data |= TD_CV; tp->tte_vpn = TV_VPN(va, TS_8K); tp->tte_data = data; TAILQ_INSERT_TAIL(&m->md.tte_list, tp, tte_link); } /* * Map a wired page into kernel virtual address space. This additionally * takes a flag argument which is or'ed to the TTE data. This is used by * sparc64_bus_mem_map(). * NOTE: if the mapping is non-cacheable, it's the caller's responsibility * to flush entries that might still be in the cache, if applicable. */ void pmap_kenter_flags(vm_offset_t va, vm_paddr_t pa, u_long flags) { struct tte *tp; tp = tsb_kvtotte(va); CTR4(KTR_PMAP, "pmap_kenter_flags: va=%#lx pa=%#lx tp=%p data=%#lx", va, pa, tp, tp->tte_data); tp->tte_vpn = TV_VPN(va, TS_8K); tp->tte_data = TD_V | TD_8K | TD_PA(pa) | TD_REF | TD_P | flags; } /* * Remove a wired page from kernel virtual address space. */ void pmap_kremove(vm_offset_t va) { struct tte *tp; vm_page_t m; rw_assert(&tte_list_global_lock, RA_WLOCKED); PMAP_STATS_INC(pmap_nkremove); tp = tsb_kvtotte(va); CTR3(KTR_PMAP, "pmap_kremove: va=%#lx tp=%p data=%#lx", va, tp, tp->tte_data); if ((tp->tte_data & TD_V) == 0) return; m = PHYS_TO_VM_PAGE(TTE_GET_PA(tp)); TAILQ_REMOVE(&m->md.tte_list, tp, tte_link); pmap_cache_remove(m, va); TTE_ZERO(tp); } /* * Inverse of pmap_kenter_flags, used by bus_space_unmap(). */ void pmap_kremove_flags(vm_offset_t va) { struct tte *tp; tp = tsb_kvtotte(va); CTR3(KTR_PMAP, "pmap_kremove_flags: va=%#lx tp=%p data=%#lx", va, tp, tp->tte_data); TTE_ZERO(tp); } /* * Map a range of physical addresses into kernel virtual address space. * * The value passed in *virt is a suggested virtual address for the mapping. * Architectures which can support a direct-mapped physical to virtual region * can return the appropriate address within that region, leaving '*virt' * unchanged. */ vm_offset_t pmap_map(vm_offset_t *virt, vm_paddr_t start, vm_paddr_t end, int prot) { return (TLB_PHYS_TO_DIRECT(start)); } /* * Map a list of wired pages into kernel virtual address space. This is * intended for temporary mappings which do not need page modification or * references recorded. Existing mappings in the region are overwritten. */ void pmap_qenter(vm_offset_t sva, vm_page_t *m, int count) { vm_offset_t va; PMAP_STATS_INC(pmap_nqenter); va = sva; rw_wlock(&tte_list_global_lock); while (count-- > 0) { pmap_kenter(va, *m); va += PAGE_SIZE; m++; } rw_wunlock(&tte_list_global_lock); tlb_range_demap(kernel_pmap, sva, va); } /* * Remove page mappings from kernel virtual address space. Intended for * temporary mappings entered by pmap_qenter. */ void pmap_qremove(vm_offset_t sva, int count) { vm_offset_t va; PMAP_STATS_INC(pmap_nqremove); va = sva; rw_wlock(&tte_list_global_lock); while (count-- > 0) { pmap_kremove(va); va += PAGE_SIZE; } rw_wunlock(&tte_list_global_lock); tlb_range_demap(kernel_pmap, sva, va); } /* * Initialize the pmap associated with process 0. */ void pmap_pinit0(pmap_t pm) { int i; PMAP_LOCK_INIT(pm); for (i = 0; i < MAXCPU; i++) pm->pm_context[i] = TLB_CTX_KERNEL; CPU_ZERO(&pm->pm_active); pm->pm_tsb = NULL; pm->pm_tsb_obj = NULL; bzero(&pm->pm_stats, sizeof(pm->pm_stats)); } /* * Initialize a preallocated and zeroed pmap structure, such as one in a * vmspace structure. */ int pmap_pinit(pmap_t pm) { vm_page_t ma[TSB_PAGES]; vm_page_t m; int i; /* * Allocate KVA space for the TSB. */ if (pm->pm_tsb == NULL) { pm->pm_tsb = (struct tte *)kva_alloc(TSB_BSIZE); if (pm->pm_tsb == NULL) return (0); } /* * Allocate an object for it. */ if (pm->pm_tsb_obj == NULL) pm->pm_tsb_obj = vm_object_allocate(OBJT_PHYS, TSB_PAGES); for (i = 0; i < MAXCPU; i++) pm->pm_context[i] = -1; CPU_ZERO(&pm->pm_active); VM_OBJECT_WLOCK(pm->pm_tsb_obj); for (i = 0; i < TSB_PAGES; i++) { m = vm_page_grab(pm->pm_tsb_obj, i, VM_ALLOC_NOBUSY | VM_ALLOC_WIRED | VM_ALLOC_ZERO); m->valid = VM_PAGE_BITS_ALL; m->md.pmap = pm; ma[i] = m; } VM_OBJECT_WUNLOCK(pm->pm_tsb_obj); pmap_qenter((vm_offset_t)pm->pm_tsb, ma, TSB_PAGES); bzero(&pm->pm_stats, sizeof(pm->pm_stats)); return (1); } /* * Release any resources held by the given physical map. * Called when a pmap initialized by pmap_pinit is being released. * Should only be called if the map contains no valid mappings. */ void pmap_release(pmap_t pm) { vm_object_t obj; vm_page_t m; #ifdef SMP struct pcpu *pc; #endif CTR2(KTR_PMAP, "pmap_release: ctx=%#x tsb=%p", pm->pm_context[curcpu], pm->pm_tsb); KASSERT(pmap_resident_count(pm) == 0, ("pmap_release: resident pages %ld != 0", pmap_resident_count(pm))); /* * After the pmap was freed, it might be reallocated to a new process. * When switching, this might lead us to wrongly assume that we need * not switch contexts because old and new pmap pointer are equal. * Therefore, make sure that this pmap is not referenced by any PCPU * pointer any more. This could happen in two cases: * - A process that referenced the pmap is currently exiting on a CPU. * However, it is guaranteed to not switch in any more after setting * its state to PRS_ZOMBIE. * - A process that referenced this pmap ran on a CPU, but we switched * to a kernel thread, leaving the pmap pointer unchanged. */ #ifdef SMP sched_pin(); STAILQ_FOREACH(pc, &cpuhead, pc_allcpu) atomic_cmpset_rel_ptr((uintptr_t *)&pc->pc_pmap, (uintptr_t)pm, (uintptr_t)NULL); sched_unpin(); #else critical_enter(); if (PCPU_GET(pmap) == pm) PCPU_SET(pmap, NULL); critical_exit(); #endif pmap_qremove((vm_offset_t)pm->pm_tsb, TSB_PAGES); obj = pm->pm_tsb_obj; VM_OBJECT_WLOCK(obj); KASSERT(obj->ref_count == 1, ("pmap_release: tsbobj ref count != 1")); while (!TAILQ_EMPTY(&obj->memq)) { m = TAILQ_FIRST(&obj->memq); m->md.pmap = NULL; m->wire_count--; atomic_subtract_int(&vm_cnt.v_wire_count, 1); vm_page_free_zero(m); } VM_OBJECT_WUNLOCK(obj); } /* * Grow the number of kernel page table entries. Unneeded. */ void pmap_growkernel(vm_offset_t addr) { panic("pmap_growkernel: can't grow kernel"); } int pmap_remove_tte(struct pmap *pm, struct pmap *pm2, struct tte *tp, vm_offset_t va) { vm_page_t m; u_long data; rw_assert(&tte_list_global_lock, RA_WLOCKED); data = atomic_readandclear_long(&tp->tte_data); if ((data & TD_FAKE) == 0) { m = PHYS_TO_VM_PAGE(TD_PA(data)); TAILQ_REMOVE(&m->md.tte_list, tp, tte_link); if ((data & TD_WIRED) != 0) pm->pm_stats.wired_count--; if ((data & TD_PV) != 0) { if ((data & TD_W) != 0) vm_page_dirty(m); if ((data & TD_REF) != 0) vm_page_aflag_set(m, PGA_REFERENCED); if (TAILQ_EMPTY(&m->md.tte_list)) vm_page_aflag_clear(m, PGA_WRITEABLE); pm->pm_stats.resident_count--; } pmap_cache_remove(m, va); } TTE_ZERO(tp); if (PMAP_REMOVE_DONE(pm)) return (0); return (1); } /* * Remove the given range of addresses from the specified map. */ void pmap_remove(pmap_t pm, vm_offset_t start, vm_offset_t end) { struct tte *tp; vm_offset_t va; CTR3(KTR_PMAP, "pmap_remove: ctx=%#lx start=%#lx end=%#lx", pm->pm_context[curcpu], start, end); if (PMAP_REMOVE_DONE(pm)) return; rw_wlock(&tte_list_global_lock); PMAP_LOCK(pm); if (end - start > PMAP_TSB_THRESH) { tsb_foreach(pm, NULL, start, end, pmap_remove_tte); tlb_context_demap(pm); } else { for (va = start; va < end; va += PAGE_SIZE) if ((tp = tsb_tte_lookup(pm, va)) != NULL && !pmap_remove_tte(pm, NULL, tp, va)) break; tlb_range_demap(pm, start, end - 1); } PMAP_UNLOCK(pm); rw_wunlock(&tte_list_global_lock); } void pmap_remove_all(vm_page_t m) { struct pmap *pm; struct tte *tpn; struct tte *tp; vm_offset_t va; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_remove_all: page %p is not managed", m)); rw_wlock(&tte_list_global_lock); for (tp = TAILQ_FIRST(&m->md.tte_list); tp != NULL; tp = tpn) { tpn = TAILQ_NEXT(tp, tte_link); if ((tp->tte_data & TD_PV) == 0) continue; pm = TTE_GET_PMAP(tp); va = TTE_GET_VA(tp); PMAP_LOCK(pm); if ((tp->tte_data & TD_WIRED) != 0) pm->pm_stats.wired_count--; if ((tp->tte_data & TD_REF) != 0) vm_page_aflag_set(m, PGA_REFERENCED); if ((tp->tte_data & TD_W) != 0) vm_page_dirty(m); tp->tte_data &= ~TD_V; tlb_page_demap(pm, va); TAILQ_REMOVE(&m->md.tte_list, tp, tte_link); pm->pm_stats.resident_count--; pmap_cache_remove(m, va); TTE_ZERO(tp); PMAP_UNLOCK(pm); } vm_page_aflag_clear(m, PGA_WRITEABLE); rw_wunlock(&tte_list_global_lock); } static int pmap_protect_tte(struct pmap *pm, struct pmap *pm2, struct tte *tp, vm_offset_t va) { u_long data; vm_page_t m; PMAP_LOCK_ASSERT(pm, MA_OWNED); data = atomic_clear_long(&tp->tte_data, TD_SW | TD_W); if ((data & (TD_PV | TD_W)) == (TD_PV | TD_W)) { m = PHYS_TO_VM_PAGE(TD_PA(data)); vm_page_dirty(m); } return (1); } /* * Set the physical protection on the specified range of this map as requested. */ void pmap_protect(pmap_t pm, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot) { vm_offset_t va; struct tte *tp; CTR4(KTR_PMAP, "pmap_protect: ctx=%#lx sva=%#lx eva=%#lx prot=%#lx", pm->pm_context[curcpu], sva, eva, prot); if ((prot & VM_PROT_READ) == VM_PROT_NONE) { pmap_remove(pm, sva, eva); return; } if (prot & VM_PROT_WRITE) return; PMAP_LOCK(pm); if (eva - sva > PMAP_TSB_THRESH) { tsb_foreach(pm, NULL, sva, eva, pmap_protect_tte); tlb_context_demap(pm); } else { for (va = sva; va < eva; va += PAGE_SIZE) if ((tp = tsb_tte_lookup(pm, va)) != NULL) pmap_protect_tte(pm, NULL, tp, va); tlb_range_demap(pm, sva, eva - 1); } PMAP_UNLOCK(pm); } /* * Map the given physical page at the specified virtual address in the * target pmap with the protection requested. If specified the page * will be wired down. */ int pmap_enter(pmap_t pm, vm_offset_t va, vm_page_t m, vm_prot_t prot, u_int flags, int8_t psind) { int rv; rw_wlock(&tte_list_global_lock); PMAP_LOCK(pm); rv = pmap_enter_locked(pm, va, m, prot, flags, psind); rw_wunlock(&tte_list_global_lock); PMAP_UNLOCK(pm); return (rv); } /* * Map the given physical page at the specified virtual address in the * target pmap with the protection requested. If specified the page * will be wired down. * * The page queues and pmap must be locked. */ static int pmap_enter_locked(pmap_t pm, vm_offset_t va, vm_page_t m, vm_prot_t prot, u_int flags, int8_t psind __unused) { struct tte *tp; vm_paddr_t pa; vm_page_t real; u_long data; boolean_t wired; rw_assert(&tte_list_global_lock, RA_WLOCKED); PMAP_LOCK_ASSERT(pm, MA_OWNED); if ((m->oflags & VPO_UNMANAGED) == 0 && !vm_page_xbusied(m)) VM_OBJECT_ASSERT_LOCKED(m->object); PMAP_STATS_INC(pmap_nenter); pa = VM_PAGE_TO_PHYS(m); wired = (flags & PMAP_ENTER_WIRED) != 0; /* * If this is a fake page from the device_pager, but it covers actual * physical memory, convert to the real backing page. */ if ((m->flags & PG_FICTITIOUS) != 0) { real = vm_phys_paddr_to_vm_page(pa); if (real != NULL) m = real; } CTR6(KTR_PMAP, "pmap_enter_locked: ctx=%p m=%p va=%#lx pa=%#lx prot=%#x wired=%d", pm->pm_context[curcpu], m, va, pa, prot, wired); /* * If there is an existing mapping, and the physical address has not * changed, must be protection or wiring change. */ if ((tp = tsb_tte_lookup(pm, va)) != NULL && TTE_GET_PA(tp) == pa) { CTR0(KTR_PMAP, "pmap_enter_locked: update"); PMAP_STATS_INC(pmap_nenter_update); /* * Wiring change, just update stats. */ if (wired) { if ((tp->tte_data & TD_WIRED) == 0) { tp->tte_data |= TD_WIRED; pm->pm_stats.wired_count++; } } else { if ((tp->tte_data & TD_WIRED) != 0) { tp->tte_data &= ~TD_WIRED; pm->pm_stats.wired_count--; } } /* * Save the old bits and clear the ones we're interested in. */ data = tp->tte_data; tp->tte_data &= ~(TD_EXEC | TD_SW | TD_W); /* * If we're turning off write permissions, sense modify status. */ if ((prot & VM_PROT_WRITE) != 0) { tp->tte_data |= TD_SW; if (wired) tp->tte_data |= TD_W; if ((m->oflags & VPO_UNMANAGED) == 0) vm_page_aflag_set(m, PGA_WRITEABLE); } else if ((data & TD_W) != 0) vm_page_dirty(m); /* * If we're turning on execute permissions, flush the icache. */ if ((prot & VM_PROT_EXECUTE) != 0) { if ((data & TD_EXEC) == 0) icache_page_inval(pa); tp->tte_data |= TD_EXEC; } /* * Delete the old mapping. */ tlb_page_demap(pm, TTE_GET_VA(tp)); } else { /* * If there is an existing mapping, but its for a different * physical address, delete the old mapping. */ if (tp != NULL) { CTR0(KTR_PMAP, "pmap_enter_locked: replace"); PMAP_STATS_INC(pmap_nenter_replace); pmap_remove_tte(pm, NULL, tp, va); tlb_page_demap(pm, va); } else { CTR0(KTR_PMAP, "pmap_enter_locked: new"); PMAP_STATS_INC(pmap_nenter_new); } /* * Now set up the data and install the new mapping. */ data = TD_V | TD_8K | TD_PA(pa); if (pm == kernel_pmap) data |= TD_P; if ((prot & VM_PROT_WRITE) != 0) { data |= TD_SW; if ((m->oflags & VPO_UNMANAGED) == 0) vm_page_aflag_set(m, PGA_WRITEABLE); } if (prot & VM_PROT_EXECUTE) { data |= TD_EXEC; icache_page_inval(pa); } /* * If its wired update stats. We also don't need reference or * modify tracking for wired mappings, so set the bits now. */ if (wired) { pm->pm_stats.wired_count++; data |= TD_REF | TD_WIRED; if ((prot & VM_PROT_WRITE) != 0) data |= TD_W; } tsb_tte_enter(pm, m, va, TS_8K, data); } return (KERN_SUCCESS); } /* * Maps a sequence of resident pages belonging to the same object. * The sequence begins with the given page m_start. This page is * mapped at the given virtual address start. Each subsequent page is * mapped at a virtual address that is offset from start by the same * amount as the page is offset from m_start within the object. The * last page in the sequence is the page with the largest offset from * m_start that can be mapped at a virtual address less than the given * virtual address end. Not every virtual page between start and end * is mapped; only those for which a resident page exists with the * corresponding offset from m_start are mapped. */ void pmap_enter_object(pmap_t pm, vm_offset_t start, vm_offset_t end, vm_page_t m_start, vm_prot_t prot) { vm_page_t m; vm_pindex_t diff, psize; VM_OBJECT_ASSERT_LOCKED(m_start->object); psize = atop(end - start); m = m_start; rw_wlock(&tte_list_global_lock); PMAP_LOCK(pm); while (m != NULL && (diff = m->pindex - m_start->pindex) < psize) { pmap_enter_locked(pm, start + ptoa(diff), m, prot & (VM_PROT_READ | VM_PROT_EXECUTE), 0, 0); m = TAILQ_NEXT(m, listq); } rw_wunlock(&tte_list_global_lock); PMAP_UNLOCK(pm); } void pmap_enter_quick(pmap_t pm, vm_offset_t va, vm_page_t m, vm_prot_t prot) { rw_wlock(&tte_list_global_lock); PMAP_LOCK(pm); pmap_enter_locked(pm, va, m, prot & (VM_PROT_READ | VM_PROT_EXECUTE), 0, 0); rw_wunlock(&tte_list_global_lock); PMAP_UNLOCK(pm); } void pmap_object_init_pt(pmap_t pm, vm_offset_t addr, vm_object_t object, vm_pindex_t pindex, vm_size_t size) { VM_OBJECT_ASSERT_WLOCKED(object); KASSERT(object->type == OBJT_DEVICE || object->type == OBJT_SG, ("pmap_object_init_pt: non-device object")); } static int pmap_unwire_tte(pmap_t pm, pmap_t pm2, struct tte *tp, vm_offset_t va) { PMAP_LOCK_ASSERT(pm, MA_OWNED); if ((tp->tte_data & TD_WIRED) == 0) panic("pmap_unwire_tte: tp %p is missing TD_WIRED", tp); atomic_clear_long(&tp->tte_data, TD_WIRED); pm->pm_stats.wired_count--; return (1); } /* * Clear the wired attribute from the mappings for the specified range of * addresses in the given pmap. Every valid mapping within that range must * have the wired attribute set. In contrast, invalid mappings cannot have * the wired attribute set, so they are ignored. * * The wired attribute of the translation table entry is not a hardware * feature, so there is no need to invalidate any TLB entries. */ void pmap_unwire(pmap_t pm, vm_offset_t sva, vm_offset_t eva) { vm_offset_t va; struct tte *tp; PMAP_LOCK(pm); if (eva - sva > PMAP_TSB_THRESH) tsb_foreach(pm, NULL, sva, eva, pmap_unwire_tte); else { for (va = sva; va < eva; va += PAGE_SIZE) if ((tp = tsb_tte_lookup(pm, va)) != NULL) pmap_unwire_tte(pm, NULL, tp, va); } PMAP_UNLOCK(pm); } static int pmap_copy_tte(pmap_t src_pmap, pmap_t dst_pmap, struct tte *tp, vm_offset_t va) { vm_page_t m; u_long data; if ((tp->tte_data & TD_FAKE) != 0) return (1); if (tsb_tte_lookup(dst_pmap, va) == NULL) { data = tp->tte_data & ~(TD_PV | TD_REF | TD_SW | TD_CV | TD_W); m = PHYS_TO_VM_PAGE(TTE_GET_PA(tp)); tsb_tte_enter(dst_pmap, m, va, TS_8K, data); } return (1); } void pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vm_offset_t dst_addr, vm_size_t len, vm_offset_t src_addr) { struct tte *tp; vm_offset_t va; if (dst_addr != src_addr) return; rw_wlock(&tte_list_global_lock); if (dst_pmap < src_pmap) { PMAP_LOCK(dst_pmap); PMAP_LOCK(src_pmap); } else { PMAP_LOCK(src_pmap); PMAP_LOCK(dst_pmap); } if (len > PMAP_TSB_THRESH) { tsb_foreach(src_pmap, dst_pmap, src_addr, src_addr + len, pmap_copy_tte); tlb_context_demap(dst_pmap); } else { for (va = src_addr; va < src_addr + len; va += PAGE_SIZE) if ((tp = tsb_tte_lookup(src_pmap, va)) != NULL) pmap_copy_tte(src_pmap, dst_pmap, tp, va); tlb_range_demap(dst_pmap, src_addr, src_addr + len - 1); } rw_wunlock(&tte_list_global_lock); PMAP_UNLOCK(src_pmap); PMAP_UNLOCK(dst_pmap); } void pmap_zero_page(vm_page_t m) { struct tte *tp; vm_offset_t va; vm_paddr_t pa; KASSERT((m->flags & PG_FICTITIOUS) == 0, ("pmap_zero_page: fake page")); PMAP_STATS_INC(pmap_nzero_page); pa = VM_PAGE_TO_PHYS(m); if (dcache_color_ignore != 0 || m->md.color == DCACHE_COLOR(pa)) { PMAP_STATS_INC(pmap_nzero_page_c); va = TLB_PHYS_TO_DIRECT(pa); cpu_block_zero((void *)va, PAGE_SIZE); } else if (m->md.color == -1) { PMAP_STATS_INC(pmap_nzero_page_nc); aszero(ASI_PHYS_USE_EC, pa, PAGE_SIZE); } else { PMAP_STATS_INC(pmap_nzero_page_oc); PMAP_LOCK(kernel_pmap); va = pmap_temp_map_1 + (m->md.color * PAGE_SIZE); tp = tsb_kvtotte(va); tp->tte_data = TD_V | TD_8K | TD_PA(pa) | TD_CP | TD_CV | TD_W; tp->tte_vpn = TV_VPN(va, TS_8K); cpu_block_zero((void *)va, PAGE_SIZE); tlb_page_demap(kernel_pmap, va); PMAP_UNLOCK(kernel_pmap); } } void pmap_zero_page_area(vm_page_t m, int off, int size) { struct tte *tp; vm_offset_t va; vm_paddr_t pa; KASSERT((m->flags & PG_FICTITIOUS) == 0, ("pmap_zero_page_area: fake page")); KASSERT(off + size <= PAGE_SIZE, ("pmap_zero_page_area: bad off/size")); PMAP_STATS_INC(pmap_nzero_page_area); pa = VM_PAGE_TO_PHYS(m); if (dcache_color_ignore != 0 || m->md.color == DCACHE_COLOR(pa)) { PMAP_STATS_INC(pmap_nzero_page_area_c); va = TLB_PHYS_TO_DIRECT(pa); bzero((void *)(va + off), size); } else if (m->md.color == -1) { PMAP_STATS_INC(pmap_nzero_page_area_nc); aszero(ASI_PHYS_USE_EC, pa + off, size); } else { PMAP_STATS_INC(pmap_nzero_page_area_oc); PMAP_LOCK(kernel_pmap); va = pmap_temp_map_1 + (m->md.color * PAGE_SIZE); tp = tsb_kvtotte(va); tp->tte_data = TD_V | TD_8K | TD_PA(pa) | TD_CP | TD_CV | TD_W; tp->tte_vpn = TV_VPN(va, TS_8K); bzero((void *)(va + off), size); tlb_page_demap(kernel_pmap, va); PMAP_UNLOCK(kernel_pmap); } } void pmap_copy_page(vm_page_t msrc, vm_page_t mdst) { vm_offset_t vdst; vm_offset_t vsrc; vm_paddr_t pdst; vm_paddr_t psrc; struct tte *tp; KASSERT((mdst->flags & PG_FICTITIOUS) == 0, ("pmap_copy_page: fake dst page")); KASSERT((msrc->flags & PG_FICTITIOUS) == 0, ("pmap_copy_page: fake src page")); PMAP_STATS_INC(pmap_ncopy_page); pdst = VM_PAGE_TO_PHYS(mdst); psrc = VM_PAGE_TO_PHYS(msrc); if (dcache_color_ignore != 0 || (msrc->md.color == DCACHE_COLOR(psrc) && mdst->md.color == DCACHE_COLOR(pdst))) { PMAP_STATS_INC(pmap_ncopy_page_c); vdst = TLB_PHYS_TO_DIRECT(pdst); vsrc = TLB_PHYS_TO_DIRECT(psrc); cpu_block_copy((void *)vsrc, (void *)vdst, PAGE_SIZE); } else if (msrc->md.color == -1 && mdst->md.color == -1) { PMAP_STATS_INC(pmap_ncopy_page_nc); ascopy(ASI_PHYS_USE_EC, psrc, pdst, PAGE_SIZE); } else if (msrc->md.color == -1) { if (mdst->md.color == DCACHE_COLOR(pdst)) { PMAP_STATS_INC(pmap_ncopy_page_dc); vdst = TLB_PHYS_TO_DIRECT(pdst); ascopyfrom(ASI_PHYS_USE_EC, psrc, (void *)vdst, PAGE_SIZE); } else { PMAP_STATS_INC(pmap_ncopy_page_doc); PMAP_LOCK(kernel_pmap); vdst = pmap_temp_map_1 + (mdst->md.color * PAGE_SIZE); tp = tsb_kvtotte(vdst); tp->tte_data = TD_V | TD_8K | TD_PA(pdst) | TD_CP | TD_CV | TD_W; tp->tte_vpn = TV_VPN(vdst, TS_8K); ascopyfrom(ASI_PHYS_USE_EC, psrc, (void *)vdst, PAGE_SIZE); tlb_page_demap(kernel_pmap, vdst); PMAP_UNLOCK(kernel_pmap); } } else if (mdst->md.color == -1) { if (msrc->md.color == DCACHE_COLOR(psrc)) { PMAP_STATS_INC(pmap_ncopy_page_sc); vsrc = TLB_PHYS_TO_DIRECT(psrc); ascopyto((void *)vsrc, ASI_PHYS_USE_EC, pdst, PAGE_SIZE); } else { PMAP_STATS_INC(pmap_ncopy_page_soc); PMAP_LOCK(kernel_pmap); vsrc = pmap_temp_map_1 + (msrc->md.color * PAGE_SIZE); tp = tsb_kvtotte(vsrc); tp->tte_data = TD_V | TD_8K | TD_PA(psrc) | TD_CP | TD_CV | TD_W; tp->tte_vpn = TV_VPN(vsrc, TS_8K); ascopyto((void *)vsrc, ASI_PHYS_USE_EC, pdst, PAGE_SIZE); tlb_page_demap(kernel_pmap, vsrc); PMAP_UNLOCK(kernel_pmap); } } else { PMAP_STATS_INC(pmap_ncopy_page_oc); PMAP_LOCK(kernel_pmap); vdst = pmap_temp_map_1 + (mdst->md.color * PAGE_SIZE); tp = tsb_kvtotte(vdst); tp->tte_data = TD_V | TD_8K | TD_PA(pdst) | TD_CP | TD_CV | TD_W; tp->tte_vpn = TV_VPN(vdst, TS_8K); vsrc = pmap_temp_map_2 + (msrc->md.color * PAGE_SIZE); tp = tsb_kvtotte(vsrc); tp->tte_data = TD_V | TD_8K | TD_PA(psrc) | TD_CP | TD_CV | TD_W; tp->tte_vpn = TV_VPN(vsrc, TS_8K); cpu_block_copy((void *)vsrc, (void *)vdst, PAGE_SIZE); tlb_page_demap(kernel_pmap, vdst); tlb_page_demap(kernel_pmap, vsrc); PMAP_UNLOCK(kernel_pmap); } } vm_offset_t pmap_quick_enter_page(vm_page_t m) { vm_paddr_t pa; vm_offset_t qaddr; struct tte *tp; pa = VM_PAGE_TO_PHYS(m); if (dcache_color_ignore != 0 || m->md.color == DCACHE_COLOR(pa)) return (TLB_PHYS_TO_DIRECT(pa)); critical_enter(); qaddr = PCPU_GET(qmap_addr); qaddr += (PAGE_SIZE * ((DCACHE_COLORS + DCACHE_COLOR(pa) - DCACHE_COLOR(qaddr)) % DCACHE_COLORS)); tp = tsb_kvtotte(qaddr); KASSERT(tp->tte_data == 0, ("pmap_quick_enter_page: PTE busy")); tp->tte_data = TD_V | TD_8K | TD_PA(pa) | TD_CP | TD_CV | TD_W; tp->tte_vpn = TV_VPN(qaddr, TS_8K); return (qaddr); } void pmap_quick_remove_page(vm_offset_t addr) { vm_offset_t qaddr; struct tte *tp; if (addr >= VM_MIN_DIRECT_ADDRESS) return; tp = tsb_kvtotte(addr); qaddr = PCPU_GET(qmap_addr); KASSERT((addr >= qaddr) && (addr < (qaddr + (PAGE_SIZE * DCACHE_COLORS))), ("pmap_quick_remove_page: invalid address")); KASSERT(tp->tte_data != 0, ("pmap_quick_remove_page: PTE not in use")); stxa(TLB_DEMAP_VA(addr) | TLB_DEMAP_NUCLEUS | TLB_DEMAP_PAGE, ASI_DMMU_DEMAP, 0); stxa(TLB_DEMAP_VA(addr) | TLB_DEMAP_NUCLEUS | TLB_DEMAP_PAGE, ASI_IMMU_DEMAP, 0); flush(KERNBASE); TTE_ZERO(tp); critical_exit(); } int unmapped_buf_allowed; void pmap_copy_pages(vm_page_t ma[], vm_offset_t a_offset, vm_page_t mb[], vm_offset_t b_offset, int xfersize) { panic("pmap_copy_pages: not implemented"); } /* * Returns true if the pmap's pv is one of the first * 16 pvs linked to from this page. This count may * be changed upwards or downwards in the future; it * is only necessary that true be returned for a small * subset of pmaps for proper page aging. */ boolean_t pmap_page_exists_quick(pmap_t pm, vm_page_t m) { struct tte *tp; int loops; boolean_t rv; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_page_exists_quick: page %p is not managed", m)); loops = 0; rv = FALSE; rw_wlock(&tte_list_global_lock); TAILQ_FOREACH(tp, &m->md.tte_list, tte_link) { if ((tp->tte_data & TD_PV) == 0) continue; if (TTE_GET_PMAP(tp) == pm) { rv = TRUE; break; } if (++loops >= 16) break; } rw_wunlock(&tte_list_global_lock); return (rv); } /* * Return the number of managed mappings to the given physical page * that are wired. */ int pmap_page_wired_mappings(vm_page_t m) { struct tte *tp; int count; count = 0; if ((m->oflags & VPO_UNMANAGED) != 0) return (count); rw_wlock(&tte_list_global_lock); TAILQ_FOREACH(tp, &m->md.tte_list, tte_link) if ((tp->tte_data & (TD_PV | TD_WIRED)) == (TD_PV | TD_WIRED)) count++; rw_wunlock(&tte_list_global_lock); return (count); } /* * Remove all pages from specified address space, this aids process exit * speeds. This is much faster than pmap_remove in the case of running down * an entire address space. Only works for the current pmap. */ void pmap_remove_pages(pmap_t pm) { } /* * Returns TRUE if the given page has a managed mapping. */ boolean_t pmap_page_is_mapped(vm_page_t m) { struct tte *tp; boolean_t rv; rv = FALSE; if ((m->oflags & VPO_UNMANAGED) != 0) return (rv); rw_wlock(&tte_list_global_lock); TAILQ_FOREACH(tp, &m->md.tte_list, tte_link) if ((tp->tte_data & TD_PV) != 0) { rv = TRUE; break; } rw_wunlock(&tte_list_global_lock); return (rv); } +#define PMAP_TS_REFERENCED_MAX 5 + /* * Return a count of reference bits for a page, clearing those bits. * It is not necessary for every reference bit to be cleared, but it * is necessary that 0 only be returned when there are truly no * reference bits set. * * XXX: The exact number of bits to check and clear is a matter that * should be tested and standardized at some point in the future for * optimal aging of shared pages. + * + * As an optimization, update the page's dirty field if a modified bit is + * found while counting reference bits. This opportunistic update can be + * performed at low cost and can eliminate the need for some future calls + * to pmap_is_modified(). However, since this function stops after + * finding PMAP_TS_REFERENCED_MAX reference bits, it may not detect some + * dirty pages. Those dirty pages will only be detected by a future call + * to pmap_is_modified(). */ int pmap_ts_referenced(vm_page_t m) { struct tte *tpf; struct tte *tpn; struct tte *tp; u_long data; int count; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_ts_referenced: page %p is not managed", m)); count = 0; rw_wlock(&tte_list_global_lock); if ((tp = TAILQ_FIRST(&m->md.tte_list)) != NULL) { tpf = tp; do { tpn = TAILQ_NEXT(tp, tte_link); TAILQ_REMOVE(&m->md.tte_list, tp, tte_link); TAILQ_INSERT_TAIL(&m->md.tte_list, tp, tte_link); if ((tp->tte_data & TD_PV) == 0) continue; data = atomic_clear_long(&tp->tte_data, TD_REF); - if ((data & TD_REF) != 0 && ++count > 4) + if ((data & TD_W) != 0) + vm_page_dirty(m); + if ((data & TD_REF) != 0 && ++count >= + PMAP_TS_REFERENCED_MAX) break; } while ((tp = tpn) != NULL && tp != tpf); } rw_wunlock(&tte_list_global_lock); return (count); } boolean_t pmap_is_modified(vm_page_t m) { struct tte *tp; boolean_t rv; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_is_modified: page %p is not managed", m)); rv = FALSE; /* * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * concurrently set while the object is locked. Thus, if PGA_WRITEABLE * is clear, no TTEs can have TD_W set. */ VM_OBJECT_ASSERT_WLOCKED(m->object); if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return (rv); rw_wlock(&tte_list_global_lock); TAILQ_FOREACH(tp, &m->md.tte_list, tte_link) { if ((tp->tte_data & TD_PV) == 0) continue; if ((tp->tte_data & TD_W) != 0) { rv = TRUE; break; } } rw_wunlock(&tte_list_global_lock); return (rv); } /* * pmap_is_prefaultable: * * Return whether or not the specified virtual address is elgible * for prefault. */ boolean_t pmap_is_prefaultable(pmap_t pmap, vm_offset_t addr) { boolean_t rv; PMAP_LOCK(pmap); rv = tsb_tte_lookup(pmap, addr) == NULL; PMAP_UNLOCK(pmap); return (rv); } /* * Return whether or not the specified physical page was referenced * in any physical maps. */ boolean_t pmap_is_referenced(vm_page_t m) { struct tte *tp; boolean_t rv; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_is_referenced: page %p is not managed", m)); rv = FALSE; rw_wlock(&tte_list_global_lock); TAILQ_FOREACH(tp, &m->md.tte_list, tte_link) { if ((tp->tte_data & TD_PV) == 0) continue; if ((tp->tte_data & TD_REF) != 0) { rv = TRUE; break; } } rw_wunlock(&tte_list_global_lock); return (rv); } /* * This function is advisory. */ void pmap_advise(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, int advice) { } void pmap_clear_modify(vm_page_t m) { struct tte *tp; u_long data; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_clear_modify: page %p is not managed", m)); VM_OBJECT_ASSERT_WLOCKED(m->object); KASSERT(!vm_page_xbusied(m), ("pmap_clear_modify: page %p is exclusive busied", m)); /* * If the page is not PGA_WRITEABLE, then no TTEs can have TD_W set. * If the object containing the page is locked and the page is not * exclusive busied, then PGA_WRITEABLE cannot be concurrently set. */ if ((m->aflags & PGA_WRITEABLE) == 0) return; rw_wlock(&tte_list_global_lock); TAILQ_FOREACH(tp, &m->md.tte_list, tte_link) { if ((tp->tte_data & TD_PV) == 0) continue; data = atomic_clear_long(&tp->tte_data, TD_W); if ((data & TD_W) != 0) tlb_page_demap(TTE_GET_PMAP(tp), TTE_GET_VA(tp)); } rw_wunlock(&tte_list_global_lock); } void pmap_remove_write(vm_page_t m) { struct tte *tp; u_long data; KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("pmap_remove_write: page %p is not managed", m)); /* * If the page is not exclusive busied, then PGA_WRITEABLE cannot be * set by another thread while the object is locked. Thus, * if PGA_WRITEABLE is clear, no page table entries need updating. */ VM_OBJECT_ASSERT_WLOCKED(m->object); if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0) return; rw_wlock(&tte_list_global_lock); TAILQ_FOREACH(tp, &m->md.tte_list, tte_link) { if ((tp->tte_data & TD_PV) == 0) continue; data = atomic_clear_long(&tp->tte_data, TD_SW | TD_W); if ((data & TD_W) != 0) { vm_page_dirty(m); tlb_page_demap(TTE_GET_PMAP(tp), TTE_GET_VA(tp)); } } vm_page_aflag_clear(m, PGA_WRITEABLE); rw_wunlock(&tte_list_global_lock); } int pmap_mincore(pmap_t pm, vm_offset_t addr, vm_paddr_t *locked_pa) { /* TODO; */ return (0); } /* * Activate a user pmap. The pmap must be activated before its address space * can be accessed in any way. */ void pmap_activate(struct thread *td) { struct vmspace *vm; struct pmap *pm; int context; critical_enter(); vm = td->td_proc->p_vmspace; pm = vmspace_pmap(vm); context = PCPU_GET(tlb_ctx); if (context == PCPU_GET(tlb_ctx_max)) { tlb_flush_user(); context = PCPU_GET(tlb_ctx_min); } PCPU_SET(tlb_ctx, context + 1); pm->pm_context[curcpu] = context; #ifdef SMP CPU_SET_ATOMIC(PCPU_GET(cpuid), &pm->pm_active); atomic_store_acq_ptr((uintptr_t *)PCPU_PTR(pmap), (uintptr_t)pm); #else CPU_SET(PCPU_GET(cpuid), &pm->pm_active); PCPU_SET(pmap, pm); #endif stxa(AA_DMMU_TSB, ASI_DMMU, pm->pm_tsb); stxa(AA_IMMU_TSB, ASI_IMMU, pm->pm_tsb); stxa(AA_DMMU_PCXR, ASI_DMMU, (ldxa(AA_DMMU_PCXR, ASI_DMMU) & TLB_CXR_PGSZ_MASK) | context); flush(KERNBASE); critical_exit(); } void pmap_sync_icache(pmap_t pm, vm_offset_t va, vm_size_t sz) { } /* * Increase the starting virtual address of the given mapping if a * different alignment might result in more superpage mappings. */ void pmap_align_superpage(vm_object_t object, vm_ooffset_t offset, vm_offset_t *addr, vm_size_t size) { } Index: projects/clang390-import/sys/sys/param.h =================================================================== --- projects/clang390-import/sys/sys/param.h (revision 305430) +++ projects/clang390-import/sys/sys/param.h (revision 305431) @@ -1,363 +1,363 @@ /*- * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)param.h 8.3 (Berkeley) 4/4/95 * $FreeBSD$ */ #ifndef _SYS_PARAM_H_ #define _SYS_PARAM_H_ #include #define BSD 199506 /* System version (year & month). */ #define BSD4_3 1 #define BSD4_4 1 /* * __FreeBSD_version numbers are documented in the Porter's Handbook. * If you bump the version for any reason, you should update the documentation * there. * Currently this lives here in the doc/ repository: * * head/en_US.ISO8859-1/books/porters-handbook/versions/chapter.xml * * scheme is: Rxx * 'R' is in the range 0 to 4 if this is a release branch or * x.0-CURRENT before RELENG_*_0 is created, otherwise 'R' is * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1200006 /* Master, propagated to newvers */ +#define __FreeBSD_version 1200007 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, * which by definition is always true on FreeBSD. This macro is also defined * on other systems that use the kernel of FreeBSD, such as GNU/kFreeBSD. * * It is tempting to use this macro in userland code when we want to enable * kernel-specific routines, and in fact it's fine to do this in code that * is part of FreeBSD itself. However, be aware that as presence of this * macro is still not widespread (e.g. older FreeBSD versions, 3rd party * compilers, etc), it is STRONGLY DISCOURAGED to check for this macro in * external applications without also checking for __FreeBSD__ as an * alternative. */ #undef __FreeBSD_kernel__ #define __FreeBSD_kernel__ #ifdef _KERNEL #define P_OSREL_SIGWAIT 700000 #define P_OSREL_SIGSEGV 700004 #define P_OSREL_MAP_ANON 800104 #define P_OSREL_MAP_FSTRICT 1100036 #define P_OSREL_SHUTDOWN_ENOTCONN 1100077 #define P_OSREL_MAJOR(x) ((x) / 100000) #endif #ifndef LOCORE #include #endif /* * Machine-independent constants (some used in following include files). * Redefined constants are from POSIX 1003.1 limits file. * * MAXCOMLEN should be >= sizeof(ac_comm) (see ) */ #include #define MAXCOMLEN 19 /* max command name remembered */ #define MAXINTERP PATH_MAX /* max interpreter file name length */ #define MAXLOGNAME 33 /* max login name length (incl. NUL) */ #define MAXUPRC CHILD_MAX /* max simultaneous processes */ #define NCARGS ARG_MAX /* max bytes for an exec function */ #define NGROUPS (NGROUPS_MAX+1) /* max number groups */ #define NOFILE OPEN_MAX /* max open files per process */ #define NOGROUP 65535 /* marker for empty group set member */ #define MAXHOSTNAMELEN 256 /* max hostname size */ #define SPECNAMELEN 63 /* max length of devicename */ /* More types and definitions used throughout the kernel. */ #ifdef _KERNEL #include #include #ifndef LOCORE #include #include #endif #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #endif #ifndef _KERNEL /* Signals. */ #include #endif /* Machine type dependent parameters. */ #include #ifndef _KERNEL #include #endif #ifndef DEV_BSHIFT #define DEV_BSHIFT 9 /* log2(DEV_BSIZE) */ #endif #define DEV_BSIZE (1<>PAGE_SHIFT) #endif /* * btodb() is messy and perhaps slow because `bytes' may be an off_t. We * want to shift an unsigned type to avoid sign extension and we don't * want to widen `bytes' unnecessarily. Assume that the result fits in * a daddr_t. */ #ifndef btodb #define btodb(bytes) /* calculates (bytes / DEV_BSIZE) */ \ (sizeof (bytes) > sizeof(long) \ ? (daddr_t)((unsigned long long)(bytes) >> DEV_BSHIFT) \ : (daddr_t)((unsigned long)(bytes) >> DEV_BSHIFT)) #endif #ifndef dbtob #define dbtob(db) /* calculates (db * DEV_BSIZE) */ \ ((off_t)(db) << DEV_BSHIFT) #endif #define PRIMASK 0x0ff #define PCATCH 0x100 /* OR'd with pri for tsleep to check signals */ #define PDROP 0x200 /* OR'd with pri to stop re-entry of interlock mutex */ #define NZERO 0 /* default "nice" */ #define NBBY 8 /* number of bits in a byte */ #define NBPW sizeof(int) /* number of bytes per word (integer) */ #define CMASK 022 /* default file mask: S_IWGRP|S_IWOTH */ #define NODEV (dev_t)(-1) /* non-existent device */ /* * File system parameters and macros. * * MAXBSIZE - Filesystems are made out of blocks of at most MAXBSIZE bytes * per block. MAXBSIZE may be made larger without effecting * any existing filesystems as long as it does not exceed MAXPHYS, * and may be made smaller at the risk of not being able to use * filesystems which require a block size exceeding MAXBSIZE. * * MAXBCACHEBUF - Maximum size of a buffer in the buffer cache. This must * be >= MAXBSIZE and can be set differently for different * architectures by defining it in . * Making this larger allows NFS to do larger reads/writes. * * BKVASIZE - Nominal buffer space per buffer, in bytes. BKVASIZE is the * minimum KVM memory reservation the kernel is willing to make. * Filesystems can of course request smaller chunks. Actual * backing memory uses a chunk size of a page (PAGE_SIZE). * The default value here can be overridden on a per-architecture * basis by defining it in . This should * probably be done to increase its value, when MAXBCACHEBUF is * defined as a larger value in . * * If you make BKVASIZE too small you risk seriously fragmenting * the buffer KVM map which may slow things down a bit. If you * make it too big the kernel will not be able to optimally use * the KVM memory reserved for the buffer cache and will wind * up with too-few buffers. * * The default is 16384, roughly 2x the block size used by a * normal UFS filesystem. */ #define MAXBSIZE 65536 /* must be power of 2 */ #ifndef MAXBCACHEBUF #define MAXBCACHEBUF MAXBSIZE /* must be a power of 2 >= MAXBSIZE */ #endif #ifndef BKVASIZE #define BKVASIZE 16384 /* must be power of 2 */ #endif #define BKVAMASK (BKVASIZE-1) /* * MAXPATHLEN defines the longest permissible path length after expanding * symbolic links. It is used to allocate a temporary buffer from the buffer * pool in which to do the name expansion, hence should be a power of two, * and must be less than or equal to MAXBSIZE. MAXSYMLINKS defines the * maximum number of symbolic links that may be expanded in a path name. * It should be set high enough to allow all legitimate uses, but halt * infinite loops reasonably quickly. */ #define MAXPATHLEN PATH_MAX #define MAXSYMLINKS 32 /* Bit map related macros. */ #define setbit(a,i) (((unsigned char *)(a))[(i)/NBBY] |= 1<<((i)%NBBY)) #define clrbit(a,i) (((unsigned char *)(a))[(i)/NBBY] &= ~(1<<((i)%NBBY))) #define isset(a,i) \ (((const unsigned char *)(a))[(i)/NBBY] & (1<<((i)%NBBY))) #define isclr(a,i) \ ((((const unsigned char *)(a))[(i)/NBBY] & (1<<((i)%NBBY))) == 0) /* Macros for counting and rounding. */ #ifndef howmany #define howmany(x, y) (((x)+((y)-1))/(y)) #endif #define nitems(x) (sizeof((x)) / sizeof((x)[0])) #define rounddown(x, y) (((x)/(y))*(y)) #define rounddown2(x, y) ((x)&(~((y)-1))) /* if y is power of two */ #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ #define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */ #define powerof2(x) ((((x)-1)&(x))==0) /* Macros for min/max. */ #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) #ifdef _KERNEL /* * Basic byte order function prototypes for non-inline functions. */ #ifndef LOCORE #ifndef _BYTEORDER_PROTOTYPED #define _BYTEORDER_PROTOTYPED __BEGIN_DECLS __uint32_t htonl(__uint32_t); __uint16_t htons(__uint16_t); __uint32_t ntohl(__uint32_t); __uint16_t ntohs(__uint16_t); __END_DECLS #endif #endif #ifndef lint #ifndef _BYTEORDER_FUNC_DEFINED #define _BYTEORDER_FUNC_DEFINED #define htonl(x) __htonl(x) #define htons(x) __htons(x) #define ntohl(x) __ntohl(x) #define ntohs(x) __ntohs(x) #endif /* !_BYTEORDER_FUNC_DEFINED */ #endif /* lint */ #endif /* _KERNEL */ /* * Scale factor for scaled integers used to count %cpu time and load avgs. * * The number of CPU `tick's that map to a unique `%age' can be expressed * by the formula (1 / (2 ^ (FSHIFT - 11))). The maximum load average that * can be calculated (assuming 32 bits) can be closely approximated using * the formula (2 ^ (2 * (16 - FSHIFT))) for (FSHIFT < 15). * * For the scheduler to maintain a 1:1 mapping of CPU `tick' to `%age', * FSHIFT must be at least 11; this gives us a maximum load avg of ~1024. */ #define FSHIFT 11 /* bits to right of fixed binary point */ #define FSCALE (1<> (PAGE_SHIFT - DEV_BSHIFT)) #define ctodb(db) /* calculates pages to devblks */ \ ((db) << (PAGE_SHIFT - DEV_BSHIFT)) /* * Old spelling of __containerof(). */ #define member2struct(s, m, x) \ ((struct s *)(void *)((char *)(x) - offsetof(struct s, m))) /* * Access a variable length array that has been declared as a fixed * length array. */ #define __PAST_END(array, offset) (((__typeof__(*(array)) *)(array))[offset]) #endif /* _SYS_PARAM_H_ */ Index: projects/clang390-import/usr.bin/top/machine.c =================================================================== --- projects/clang390-import/usr.bin/top/machine.c (revision 305430) +++ projects/clang390-import/usr.bin/top/machine.c (revision 305431) @@ -1,1602 +1,1649 @@ /* * top - a top users display for Unix * * SYNOPSIS: For FreeBSD-2.x and later * * DESCRIPTION: * Originally written for BSD4.4 system by Christos Zoulas. * Ported to FreeBSD 2.x by Steven Wallace && Wolfram Schneider * Order support hacked in from top-3.5beta6/machine/m_aix41.c * by Monte Mitzelfelt (for latest top see http://www.groupsys.com/topinfo/) * * This is the machine-dependent module for FreeBSD 2.2 * Works for: * FreeBSD 2.2.x, 3.x, 4.x, and probably FreeBSD 2.1.x * * LIBS: -lkvm * * AUTHOR: Christos Zoulas * Steven Wallace * Wolfram Schneider * Thomas Moestl * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "top.h" #include "machine.h" #include "screen.h" #include "utils.h" #include "layout.h" #define GETSYSCTL(name, var) getsysctl(name, &(var), sizeof(var)) #define SMPUNAMELEN 13 #define UPUNAMELEN 15 extern struct process_select ps; extern char* printable(char *); static int smpmode; enum displaymodes displaymode; #ifdef TOP_USERNAME_LEN static int namelength = TOP_USERNAME_LEN; #else static int namelength = 8; #endif /* TOP_JID_LEN based on max of 999999 */ #define TOP_JID_LEN 7 +#define TOP_SWAP_LEN 6 static int jidlength; +static int swaplength; static int cmdlengthdelta; /* Prototypes for top internals */ void quit(int); /* get_process_info passes back a handle. This is what it looks like: */ struct handle { struct kinfo_proc **next_proc; /* points to next valid proc pointer */ int remaining; /* number of pointers remaining */ }; /* declarations for load_avg */ #include "loadavg.h" /* define what weighted cpu is. */ #define weighted_cpu(pct, pp) ((pp)->ki_swtime == 0 ? 0.0 : \ ((pct) / (1.0 - exp((pp)->ki_swtime * logcpu)))) /* what we consider to be process size: */ #define PROCSIZE(pp) ((pp)->ki_size / 1024) #define RU(pp) (&(pp)->ki_rusage) #define RUTOT(pp) \ (RU(pp)->ru_inblock + RU(pp)->ru_oublock + RU(pp)->ru_majflt) #define PCTCPU(pp) (pcpu[pp - pbase]) /* definitions for indices in the nlist array */ /* * These definitions control the format of the per-process area */ static char io_header[] = " PID%*s %-*.*s VCSW IVCSW READ WRITE FAULT TOTAL PERCENT COMMAND"; #define io_Proc_format \ "%5d%*s %-*.*s %6ld %6ld %6ld %6ld %6ld %6ld %6.2f%% %.*s" static char smp_header_thr[] = - " PID%*s %-*.*s THR PRI NICE SIZE RES STATE C TIME %7s COMMAND"; + " PID%*s %-*.*s THR PRI NICE SIZE RES%*s STATE C TIME %7s COMMAND"; static char smp_header[] = - " PID%*s %-*.*s " "PRI NICE SIZE RES STATE C TIME %7s COMMAND"; + " PID%*s %-*.*s " "PRI NICE SIZE RES%*s STATE C TIME %7s COMMAND"; #define smp_Proc_format \ - "%5d%*s %-*.*s %s%3d %4s%7s %6s %-6.6s %2d%7s %6.2f%% %.*s" + "%5d%*s %-*.*s %s%3d %4s%7s %6s%*.*s %-6.6s %2d%7s %6.2f%% %.*s" static char up_header_thr[] = - " PID%*s %-*.*s THR PRI NICE SIZE RES STATE TIME %7s COMMAND"; + " PID%*s %-*.*s THR PRI NICE SIZE RES%*s STATE TIME %7s COMMAND"; static char up_header[] = - " PID%*s %-*.*s " "PRI NICE SIZE RES STATE TIME %7s COMMAND"; + " PID%*s %-*.*s " "PRI NICE SIZE RES%*s STATE TIME %7s COMMAND"; #define up_Proc_format \ - "%5d%*s %-*.*s %s%3d %4s%7s %6s %-6.6s%.0d%7s %6.2f%% %.*s" + "%5d%*s %-*.*s %s%3d %4s%7s %6s%*.*s %-6.6s%.0d%7s %6.2f%% %.*s" /* process state names for the "STATE" column of the display */ /* the extra nulls in the string "run" are for adding a slash and the processor number when needed */ char *state_abbrev[] = { "", "START", "RUN\0\0\0", "SLEEP", "STOP", "ZOMB", "WAIT", "LOCK" }; static kvm_t *kd; /* values that we stash away in _init and use in later routines */ static double logcpu; /* these are retrieved from the kernel in _init */ static load_avg ccpu; /* these are used in the get_ functions */ static int lastpid; /* these are for calculating cpu state percentages */ static long cp_time[CPUSTATES]; static long cp_old[CPUSTATES]; static long cp_diff[CPUSTATES]; /* these are for detailing the process states */ int process_states[8]; char *procstatenames[] = { "", " starting, ", " running, ", " sleeping, ", " stopped, ", " zombie, ", " waiting, ", " lock, ", NULL }; /* these are for detailing the cpu states */ int cpu_states[CPUSTATES]; char *cpustatenames[] = { "user", "nice", "system", "interrupt", "idle", NULL }; /* these are for detailing the memory statistics */ int memory_stats[7]; char *memorynames[] = { "K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", "K Free", NULL }; int arc_stats[7]; char *arcnames[] = { "K Total, ", "K MFU, ", "K MRU, ", "K Anon, ", "K Header, ", "K Other", NULL }; int swap_stats[7]; char *swapnames[] = { "K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out", NULL }; /* these are for keeping track of the proc array */ static int nproc; static int onproc = -1; static int pref_len; static struct kinfo_proc *pbase; static struct kinfo_proc **pref; static struct kinfo_proc *previous_procs; static struct kinfo_proc **previous_pref; static int previous_proc_count = 0; static int previous_proc_count_max = 0; static int previous_thread; /* data used for recalculating pctcpu */ static double *pcpu; static struct timespec proc_uptime; static struct timeval proc_wall_time; static struct timeval previous_wall_time; static uint64_t previous_interval = 0; /* total number of io operations */ static long total_inblock; static long total_oublock; static long total_majflt; /* these are for getting the memory statistics */ static int arc_enabled; static int pageshift; /* log base 2 of the pagesize */ /* define pagetok in terms of pageshift */ #define pagetok(size) ((size) << pageshift) +/* swap usage */ +#define ki_swap(kip) \ + ((kip)->ki_swrss > (kip)->ki_rssize ? (kip)->ki_swrss - (kip)->ki_rssize : 0) + /* useful externals */ long percentages(); #ifdef ORDER /* * Sorting orders. The first element is the default. */ char *ordernames[] = { "cpu", "size", "res", "time", "pri", "threads", "total", "read", "write", "fault", "vcsw", "ivcsw", - "jid", "pid", NULL + "jid", "swap", "pid", NULL }; #endif /* Per-cpu time states */ static int maxcpu; static int maxid; static int ncpus; static u_long cpumask; static long *times; static long *pcpu_cp_time; static long *pcpu_cp_old; static long *pcpu_cp_diff; static int *pcpu_cpu_states; +static int compare_swap(const void *a, const void *b); static int compare_jid(const void *a, const void *b); static int compare_pid(const void *a, const void *b); static int compare_tid(const void *a, const void *b); static const char *format_nice(const struct kinfo_proc *pp); static void getsysctl(const char *name, void *ptr, size_t len); static int swapmode(int *retavail, int *retfree); static void update_layout(void); void toggle_pcpustats(void) { if (ncpus == 1) return; update_layout(); } /* Adjust display based on ncpus and the ARC state. */ static void update_layout(void) { y_mem = 3; y_arc = 4; y_swap = 4 + arc_enabled; y_idlecursor = 5 + arc_enabled; y_message = 5 + arc_enabled; y_header = 6 + arc_enabled; y_procs = 7 + arc_enabled; Header_lines = 7 + arc_enabled; if (pcpu_stats) { y_mem += ncpus - 1; y_arc += ncpus - 1; y_swap += ncpus - 1; y_idlecursor += ncpus - 1; y_message += ncpus - 1; y_header += ncpus - 1; y_procs += ncpus - 1; Header_lines += ncpus - 1; } } int machine_init(struct statics *statics, char do_unames) { int i, j, empty, pagesize; uint64_t arc_size; size_t size; struct passwd *pw; size = sizeof(smpmode); if ((sysctlbyname("machdep.smp_active", &smpmode, &size, NULL, 0) != 0 && sysctlbyname("kern.smp.active", &smpmode, &size, NULL, 0) != 0) || size != sizeof(smpmode)) smpmode = 0; size = sizeof(arc_size); if (sysctlbyname("kstat.zfs.misc.arcstats.size", &arc_size, &size, NULL, 0) == 0 && arc_size != 0) arc_enabled = 1; if (do_unames) { while ((pw = getpwent()) != NULL) { if (strlen(pw->pw_name) > namelength) namelength = strlen(pw->pw_name); } } if (smpmode && namelength > SMPUNAMELEN) namelength = SMPUNAMELEN; else if (namelength > UPUNAMELEN) namelength = UPUNAMELEN; kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); if (kd == NULL) return (-1); GETSYSCTL("kern.ccpu", ccpu); /* this is used in calculating WCPU -- calculate it ahead of time */ logcpu = log(loaddouble(ccpu)); pbase = NULL; pref = NULL; pcpu = NULL; nproc = 0; onproc = -1; /* get the page size and calculate pageshift from it */ pagesize = getpagesize(); pageshift = 0; while (pagesize > 1) { pageshift++; pagesize >>= 1; } /* we only need the amount of log(2)1024 for our conversion */ pageshift -= LOG1024; /* fill in the statics information */ statics->procstate_names = procstatenames; statics->cpustate_names = cpustatenames; statics->memory_names = memorynames; if (arc_enabled) statics->arc_names = arcnames; else statics->arc_names = NULL; statics->swap_names = swapnames; #ifdef ORDER statics->order_names = ordernames; #endif /* Allocate state for per-CPU stats. */ cpumask = 0; ncpus = 0; GETSYSCTL("kern.smp.maxcpus", maxcpu); size = sizeof(long) * maxcpu * CPUSTATES; times = malloc(size); if (times == NULL) err(1, "malloc %zu bytes", size); if (sysctlbyname("kern.cp_times", times, &size, NULL, 0) == -1) err(1, "sysctlbyname kern.cp_times"); pcpu_cp_time = calloc(1, size); maxid = (size / CPUSTATES / sizeof(long)) - 1; for (i = 0; i <= maxid; i++) { empty = 1; for (j = 0; empty && j < CPUSTATES; j++) { if (times[i * CPUSTATES + j] != 0) empty = 0; } if (!empty) { cpumask |= (1ul << i); ncpus++; } } size = sizeof(long) * ncpus * CPUSTATES; pcpu_cp_old = calloc(1, size); pcpu_cp_diff = calloc(1, size); pcpu_cpu_states = calloc(1, size); statics->ncpus = ncpus; update_layout(); /* all done! */ return (0); } char * format_header(char *uname_field) { static char Header[128]; const char *prehead; if (ps.jail) jidlength = TOP_JID_LEN + 1; /* +1 for extra left space. */ else jidlength = 0; + if (ps.swap) + swaplength = TOP_SWAP_LEN + 1; /* +1 for extra left space */ + else + swaplength = 0; + switch (displaymode) { case DISP_CPU: /* * The logic of picking the right header format seems reverse * here because we only want to display a THR column when * "thread mode" is off (and threads are not listed as * separate lines). */ prehead = smpmode ? (ps.thread ? smp_header : smp_header_thr) : (ps.thread ? up_header : up_header_thr); snprintf(Header, sizeof(Header), prehead, jidlength, ps.jail ? " JID" : "", namelength, namelength, uname_field, + swaplength, ps.swap ? " SWAP" : "", ps.wcpu ? "WCPU" : "CPU"); break; case DISP_IO: prehead = io_header; snprintf(Header, sizeof(Header), prehead, jidlength, ps.jail ? " JID" : "", namelength, namelength, uname_field); break; } cmdlengthdelta = strlen(Header) - 7; return (Header); } static int swappgsin = -1; static int swappgsout = -1; extern struct timeval timeout; void get_system_info(struct system_info *si) { long total; struct loadavg sysload; int mib[2]; struct timeval boottime; uint64_t arc_stat, arc_stat2; int i, j; size_t size; /* get the CPU stats */ size = (maxid + 1) * CPUSTATES * sizeof(long); if (sysctlbyname("kern.cp_times", pcpu_cp_time, &size, NULL, 0) == -1) err(1, "sysctlbyname kern.cp_times"); GETSYSCTL("kern.cp_time", cp_time); GETSYSCTL("vm.loadavg", sysload); GETSYSCTL("kern.lastpid", lastpid); /* convert load averages to doubles */ for (i = 0; i < 3; i++) si->load_avg[i] = (double)sysload.ldavg[i] / sysload.fscale; /* convert cp_time counts to percentages */ for (i = j = 0; i <= maxid; i++) { if ((cpumask & (1ul << i)) == 0) continue; percentages(CPUSTATES, &pcpu_cpu_states[j * CPUSTATES], &pcpu_cp_time[j * CPUSTATES], &pcpu_cp_old[j * CPUSTATES], &pcpu_cp_diff[j * CPUSTATES]); j++; } percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); /* sum memory & swap statistics */ { static unsigned int swap_delay = 0; static int swapavail = 0; static int swapfree = 0; static long bufspace = 0; static int nspgsin, nspgsout; GETSYSCTL("vfs.bufspace", bufspace); GETSYSCTL("vm.stats.vm.v_active_count", memory_stats[0]); GETSYSCTL("vm.stats.vm.v_inactive_count", memory_stats[1]); GETSYSCTL("vm.stats.vm.v_wire_count", memory_stats[2]); GETSYSCTL("vm.stats.vm.v_cache_count", memory_stats[3]); GETSYSCTL("vm.stats.vm.v_free_count", memory_stats[5]); GETSYSCTL("vm.stats.vm.v_swappgsin", nspgsin); GETSYSCTL("vm.stats.vm.v_swappgsout", nspgsout); /* convert memory stats to Kbytes */ memory_stats[0] = pagetok(memory_stats[0]); memory_stats[1] = pagetok(memory_stats[1]); memory_stats[2] = pagetok(memory_stats[2]); memory_stats[3] = pagetok(memory_stats[3]); memory_stats[4] = bufspace / 1024; memory_stats[5] = pagetok(memory_stats[5]); memory_stats[6] = -1; /* first interval */ if (swappgsin < 0) { swap_stats[4] = 0; swap_stats[5] = 0; } /* compute differences between old and new swap statistic */ else { swap_stats[4] = pagetok(((nspgsin - swappgsin))); swap_stats[5] = pagetok(((nspgsout - swappgsout))); } swappgsin = nspgsin; swappgsout = nspgsout; /* call CPU heavy swapmode() only for changes */ if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0) { swap_stats[3] = swapmode(&swapavail, &swapfree); swap_stats[0] = swapavail; swap_stats[1] = swapavail - swapfree; swap_stats[2] = swapfree; } swap_delay = 1; swap_stats[6] = -1; } if (arc_enabled) { GETSYSCTL("kstat.zfs.misc.arcstats.size", arc_stat); arc_stats[0] = arc_stat >> 10; GETSYSCTL("vfs.zfs.mfu_size", arc_stat); arc_stats[1] = arc_stat >> 10; GETSYSCTL("vfs.zfs.mru_size", arc_stat); arc_stats[2] = arc_stat >> 10; GETSYSCTL("vfs.zfs.anon_size", arc_stat); arc_stats[3] = arc_stat >> 10; GETSYSCTL("kstat.zfs.misc.arcstats.hdr_size", arc_stat); GETSYSCTL("kstat.zfs.misc.arcstats.l2_hdr_size", arc_stat2); arc_stats[4] = arc_stat + arc_stat2 >> 10; GETSYSCTL("kstat.zfs.misc.arcstats.other_size", arc_stat); arc_stats[5] = arc_stat >> 10; si->arc = arc_stats; } /* set arrays and strings */ if (pcpu_stats) { si->cpustates = pcpu_cpu_states; si->ncpus = ncpus; } else { si->cpustates = cpu_states; si->ncpus = 1; } si->memory = memory_stats; si->swap = swap_stats; if (lastpid > 0) { si->last_pid = lastpid; } else { si->last_pid = -1; } /* * Print how long system has been up. * (Found by looking getting "boottime" from the kernel) */ mib[0] = CTL_KERN; mib[1] = KERN_BOOTTIME; size = sizeof(boottime); if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) { si->boottime = boottime; } else { si->boottime.tv_sec = -1; } } #define NOPROC ((void *)-1) /* * We need to compare data from the old process entry with the new * process entry. * To facilitate doing this quickly we stash a pointer in the kinfo_proc * structure to cache the mapping. We also use a negative cache pointer * of NOPROC to avoid duplicate lookups. * XXX: this could be done when the actual processes are fetched, we do * it here out of laziness. */ const struct kinfo_proc * get_old_proc(struct kinfo_proc *pp) { struct kinfo_proc **oldpp, *oldp; /* * If this is the first fetch of the kinfo_procs then we don't have * any previous entries. */ if (previous_proc_count == 0) return (NULL); /* negative cache? */ if (pp->ki_udata == NOPROC) return (NULL); /* cached? */ if (pp->ki_udata != NULL) return (pp->ki_udata); /* * Not cached, * 1) look up based on pid. * 2) compare process start. * If we fail here, then setup a negative cache entry, otherwise * cache it. */ oldpp = bsearch(&pp, previous_pref, previous_proc_count, sizeof(*previous_pref), ps.thread ? compare_tid : compare_pid); if (oldpp == NULL) { pp->ki_udata = NOPROC; return (NULL); } oldp = *oldpp; if (bcmp(&oldp->ki_start, &pp->ki_start, sizeof(pp->ki_start)) != 0) { pp->ki_udata = NOPROC; return (NULL); } pp->ki_udata = oldp; return (oldp); } /* * Return the total amount of IO done in blocks in/out and faults. * store the values individually in the pointers passed in. */ long get_io_stats(struct kinfo_proc *pp, long *inp, long *oup, long *flp, long *vcsw, long *ivcsw) { const struct kinfo_proc *oldp; static struct kinfo_proc dummy; long ret; oldp = get_old_proc(pp); if (oldp == NULL) { bzero(&dummy, sizeof(dummy)); oldp = &dummy; } *inp = RU(pp)->ru_inblock - RU(oldp)->ru_inblock; *oup = RU(pp)->ru_oublock - RU(oldp)->ru_oublock; *flp = RU(pp)->ru_majflt - RU(oldp)->ru_majflt; *vcsw = RU(pp)->ru_nvcsw - RU(oldp)->ru_nvcsw; *ivcsw = RU(pp)->ru_nivcsw - RU(oldp)->ru_nivcsw; ret = (RU(pp)->ru_inblock - RU(oldp)->ru_inblock) + (RU(pp)->ru_oublock - RU(oldp)->ru_oublock) + (RU(pp)->ru_majflt - RU(oldp)->ru_majflt); return (ret); } /* * If there was a previous update, use the delta in ki_runtime over * the previous interval to calculate pctcpu. Otherwise, fall back * to using the kernel's ki_pctcpu. */ static double proc_calc_pctcpu(struct kinfo_proc *pp) { const struct kinfo_proc *oldp; if (previous_interval != 0) { oldp = get_old_proc(pp); if (oldp != NULL) return ((double)(pp->ki_runtime - oldp->ki_runtime) / previous_interval); /* * If this process/thread was created during the previous * interval, charge it's total runtime to the previous * interval. */ else if (pp->ki_start.tv_sec > previous_wall_time.tv_sec || (pp->ki_start.tv_sec == previous_wall_time.tv_sec && pp->ki_start.tv_usec >= previous_wall_time.tv_usec)) return ((double)pp->ki_runtime / previous_interval); } return (pctdouble(pp->ki_pctcpu)); } /* * Return true if this process has used any CPU time since the * previous update. */ static int proc_used_cpu(struct kinfo_proc *pp) { const struct kinfo_proc *oldp; oldp = get_old_proc(pp); if (oldp == NULL) return (PCTCPU(pp) != 0); return (pp->ki_runtime != oldp->ki_runtime || RU(pp)->ru_nvcsw != RU(oldp)->ru_nvcsw || RU(pp)->ru_nivcsw != RU(oldp)->ru_nivcsw); } /* * Return the total number of block in/out and faults by a process. */ long get_io_total(struct kinfo_proc *pp) { long dummy; return (get_io_stats(pp, &dummy, &dummy, &dummy, &dummy, &dummy)); } static struct handle handle; caddr_t get_process_info(struct system_info *si, struct process_select *sel, int (*compare)(const void *, const void *)) { int i; int total_procs; long p_io; long p_inblock, p_oublock, p_majflt, p_vcsw, p_ivcsw; long nsec; int active_procs; struct kinfo_proc **prefp; struct kinfo_proc *pp; struct timespec previous_proc_uptime; /* these are copied out of sel for speed */ int show_idle; int show_jid; int show_self; int show_system; int show_uid; int show_command; int show_kidle; /* * If thread state was toggled, don't cache the previous processes. */ if (previous_thread != sel->thread) nproc = 0; previous_thread = sel->thread; /* * Save the previous process info. */ if (previous_proc_count_max < nproc) { free(previous_procs); previous_procs = malloc(nproc * sizeof(*previous_procs)); free(previous_pref); previous_pref = malloc(nproc * sizeof(*previous_pref)); if (previous_procs == NULL || previous_pref == NULL) { (void) fprintf(stderr, "top: Out of memory.\n"); quit(23); } previous_proc_count_max = nproc; } if (nproc) { for (i = 0; i < nproc; i++) previous_pref[i] = &previous_procs[i]; bcopy(pbase, previous_procs, nproc * sizeof(*previous_procs)); qsort(previous_pref, nproc, sizeof(*previous_pref), ps.thread ? compare_tid : compare_pid); } previous_proc_count = nproc; previous_proc_uptime = proc_uptime; previous_wall_time = proc_wall_time; previous_interval = 0; pbase = kvm_getprocs(kd, sel->thread ? KERN_PROC_ALL : KERN_PROC_PROC, 0, &nproc); (void)gettimeofday(&proc_wall_time, NULL); if (clock_gettime(CLOCK_UPTIME, &proc_uptime) != 0) memset(&proc_uptime, 0, sizeof(proc_uptime)); else if (previous_proc_uptime.tv_sec != 0 && previous_proc_uptime.tv_nsec != 0) { previous_interval = (proc_uptime.tv_sec - previous_proc_uptime.tv_sec) * 1000000; nsec = proc_uptime.tv_nsec - previous_proc_uptime.tv_nsec; if (nsec < 0) { previous_interval -= 1000000; nsec += 1000000000; } previous_interval += nsec / 1000; } if (nproc > onproc) { pref = realloc(pref, sizeof(*pref) * nproc); pcpu = realloc(pcpu, sizeof(*pcpu) * nproc); onproc = nproc; } if (pref == NULL || pbase == NULL || pcpu == NULL) { (void) fprintf(stderr, "top: Out of memory.\n"); quit(23); } /* get a pointer to the states summary array */ si->procstates = process_states; /* set up flags which define what we are going to select */ show_idle = sel->idle; show_jid = sel->jid != -1; show_self = sel->self == -1; show_system = sel->system; show_uid = sel->uid != -1; show_command = sel->command != NULL; show_kidle = sel->kidle; /* count up process states and get pointers to interesting procs */ total_procs = 0; active_procs = 0; total_inblock = 0; total_oublock = 0; total_majflt = 0; memset((char *)process_states, 0, sizeof(process_states)); prefp = pref; for (pp = pbase, i = 0; i < nproc; pp++, i++) { if (pp->ki_stat == 0) /* not in use */ continue; if (!show_self && pp->ki_pid == sel->self) /* skip self */ continue; if (!show_system && (pp->ki_flag & P_SYSTEM)) /* skip system process */ continue; p_io = get_io_stats(pp, &p_inblock, &p_oublock, &p_majflt, &p_vcsw, &p_ivcsw); total_inblock += p_inblock; total_oublock += p_oublock; total_majflt += p_majflt; total_procs++; process_states[pp->ki_stat]++; if (pp->ki_stat == SZOMB) /* skip zombies */ continue; if (!show_kidle && pp->ki_tdflags & TDF_IDLETD) /* skip kernel idle process */ continue; PCTCPU(pp) = proc_calc_pctcpu(pp); if (sel->thread && PCTCPU(pp) > 1.0) PCTCPU(pp) = 1.0; if (displaymode == DISP_CPU && !show_idle && (!proc_used_cpu(pp) || pp->ki_stat == SSTOP || pp->ki_stat == SIDL)) /* skip idle or non-running processes */ continue; if (displaymode == DISP_IO && !show_idle && p_io == 0) /* skip processes that aren't doing I/O */ continue; if (show_jid && pp->ki_jid != sel->jid) /* skip proc. that don't belong to the selected JID */ continue; if (show_uid && pp->ki_ruid != (uid_t)sel->uid) /* skip proc. that don't belong to the selected UID */ continue; *prefp++ = pp; active_procs++; } /* if requested, sort the "interesting" processes */ if (compare != NULL) qsort(pref, active_procs, sizeof(*pref), compare); /* remember active and total counts */ si->p_total = total_procs; si->p_active = pref_len = active_procs; /* pass back a handle */ handle.next_proc = pref; handle.remaining = active_procs; return ((caddr_t)&handle); } static char fmt[512]; /* static area where result is built */ char * format_next_process(caddr_t handle, char *(*get_userid)(int), int flags) { struct kinfo_proc *pp; const struct kinfo_proc *oldp; long cputime; double pct; struct handle *hp; char status[16]; int cpu, state; struct rusage ru, *rup; long p_tot, s_tot; - char *proc_fmt, thr_buf[6], jid_buf[TOP_JID_LEN + 1]; + char *proc_fmt, thr_buf[6]; + char jid_buf[TOP_JID_LEN + 1], swap_buf[TOP_SWAP_LEN + 1]; char *cmdbuf = NULL; char **args; const int cmdlen = 128; /* find and remember the next proc structure */ hp = (struct handle *)handle; pp = *(hp->next_proc++); hp->remaining--; /* get the process's command name */ if ((pp->ki_flag & P_INMEM) == 0) { /* * Print swapped processes as */ size_t len; len = strlen(pp->ki_comm); if (len > sizeof(pp->ki_comm) - 3) len = sizeof(pp->ki_comm) - 3; memmove(pp->ki_comm + 1, pp->ki_comm, len); pp->ki_comm[0] = '<'; pp->ki_comm[len + 1] = '>'; pp->ki_comm[len + 2] = '\0'; } /* * Convert the process's runtime from microseconds to seconds. This * time includes the interrupt time although that is not wanted here. * ps(1) is similarly sloppy. */ cputime = (pp->ki_runtime + 500000) / 1000000; /* calculate the base for cpu percentages */ pct = PCTCPU(pp); /* generate "STATE" field */ switch (state = pp->ki_stat) { case SRUN: if (smpmode && pp->ki_oncpu != NOCPU) sprintf(status, "CPU%d", pp->ki_oncpu); else strcpy(status, "RUN"); break; case SLOCK: if (pp->ki_kiflag & KI_LOCKBLOCK) { sprintf(status, "*%.6s", pp->ki_lockname); break; } /* fall through */ case SSLEEP: if (pp->ki_wmesg != NULL) { sprintf(status, "%.6s", pp->ki_wmesg); break; } /* FALLTHROUGH */ default: if (state >= 0 && state < sizeof(state_abbrev) / sizeof(*state_abbrev)) sprintf(status, "%.6s", state_abbrev[state]); else sprintf(status, "?%5d", state); break; } cmdbuf = (char *)malloc(cmdlen + 1); if (cmdbuf == NULL) { warn("malloc(%d)", cmdlen + 1); return NULL; } if (!(flags & FMT_SHOWARGS)) { if (ps.thread && pp->ki_flag & P_HADTHREADS && pp->ki_tdname[0]) { snprintf(cmdbuf, cmdlen, "%s{%s}", pp->ki_comm, pp->ki_tdname); } else { snprintf(cmdbuf, cmdlen, "%s", pp->ki_comm); } } else { if (pp->ki_flag & P_SYSTEM || pp->ki_args == NULL || (args = kvm_getargv(kd, pp, cmdlen)) == NULL || !(*args)) { if (ps.thread && pp->ki_flag & P_HADTHREADS && pp->ki_tdname[0]) { snprintf(cmdbuf, cmdlen, "[%s{%s}]", pp->ki_comm, pp->ki_tdname); } else { snprintf(cmdbuf, cmdlen, "[%s]", pp->ki_comm); } } else { char *src, *dst, *argbuf; char *cmd; size_t argbuflen; size_t len; argbuflen = cmdlen * 4; argbuf = (char *)malloc(argbuflen + 1); if (argbuf == NULL) { warn("malloc(%zu)", argbuflen + 1); free(cmdbuf); return NULL; } dst = argbuf; /* Extract cmd name from argv */ cmd = strrchr(*args, '/'); if (cmd == NULL) cmd = *args; else cmd++; for (; (src = *args++) != NULL; ) { if (*src == '\0') continue; len = (argbuflen - (dst - argbuf) - 1) / 4; strvisx(dst, src, MIN(strlen(src), len), VIS_NL | VIS_CSTYLE); while (*dst != '\0') dst++; if ((argbuflen - (dst - argbuf) - 1) / 4 > 0) *dst++ = ' '; /* add delimiting space */ } if (dst != argbuf && dst[-1] == ' ') dst--; *dst = '\0'; if (strcmp(cmd, pp->ki_comm) != 0) { if (ps.thread && pp->ki_flag & P_HADTHREADS && pp->ki_tdname[0]) snprintf(cmdbuf, cmdlen, "%s (%s){%s}", argbuf, pp->ki_comm, pp->ki_tdname); else snprintf(cmdbuf, cmdlen, "%s (%s)", argbuf, pp->ki_comm); } else { if (ps.thread && pp->ki_flag & P_HADTHREADS && pp->ki_tdname[0]) snprintf(cmdbuf, cmdlen, "%s{%s}", argbuf, pp->ki_tdname); else strlcpy(cmdbuf, argbuf, cmdlen); } free(argbuf); } } if (ps.jail == 0) jid_buf[0] = '\0'; else snprintf(jid_buf, sizeof(jid_buf), "%*d", jidlength - 1, pp->ki_jid); + if (ps.swap == 0) + swap_buf[0] = '\0'; + else + snprintf(swap_buf, sizeof(swap_buf), "%*s", + swaplength - 1, + format_k2(pagetok(ki_swap(pp)))); /* XXX */ + if (displaymode == DISP_IO) { oldp = get_old_proc(pp); if (oldp != NULL) { ru.ru_inblock = RU(pp)->ru_inblock - RU(oldp)->ru_inblock; ru.ru_oublock = RU(pp)->ru_oublock - RU(oldp)->ru_oublock; ru.ru_majflt = RU(pp)->ru_majflt - RU(oldp)->ru_majflt; ru.ru_nvcsw = RU(pp)->ru_nvcsw - RU(oldp)->ru_nvcsw; ru.ru_nivcsw = RU(pp)->ru_nivcsw - RU(oldp)->ru_nivcsw; rup = &ru; } else { rup = RU(pp); } p_tot = rup->ru_inblock + rup->ru_oublock + rup->ru_majflt; s_tot = total_inblock + total_oublock + total_majflt; snprintf(fmt, sizeof(fmt), io_Proc_format, pp->ki_pid, jidlength, jid_buf, namelength, namelength, (*get_userid)(pp->ki_ruid), rup->ru_nvcsw, rup->ru_nivcsw, rup->ru_inblock, rup->ru_oublock, rup->ru_majflt, p_tot, s_tot == 0 ? 0.0 : (p_tot * 100.0 / s_tot), screen_width > cmdlengthdelta ? screen_width - cmdlengthdelta : 0, printable(cmdbuf)); free(cmdbuf); return (fmt); } /* format this entry */ if (smpmode) { if (state == SRUN && pp->ki_oncpu != NOCPU) cpu = pp->ki_oncpu; else cpu = pp->ki_lastcpu; } else cpu = 0; proc_fmt = smpmode ? smp_Proc_format : up_Proc_format; if (ps.thread != 0) thr_buf[0] = '\0'; else snprintf(thr_buf, sizeof(thr_buf), "%*d ", (int)(sizeof(thr_buf) - 2), pp->ki_numthreads); snprintf(fmt, sizeof(fmt), proc_fmt, pp->ki_pid, jidlength, jid_buf, namelength, namelength, (*get_userid)(pp->ki_ruid), thr_buf, pp->ki_pri.pri_level - PZERO, format_nice(pp), format_k2(PROCSIZE(pp)), format_k2(pagetok(pp->ki_rssize)), + swaplength, swaplength, swap_buf, status, cpu, format_time(cputime), ps.wcpu ? 100.0 * weighted_cpu(pct, pp) : 100.0 * pct, screen_width > cmdlengthdelta ? screen_width - cmdlengthdelta : 0, printable(cmdbuf)); free(cmdbuf); /* return the result */ return (fmt); } static void getsysctl(const char *name, void *ptr, size_t len) { size_t nlen = len; if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) { fprintf(stderr, "top: sysctl(%s...) failed: %s\n", name, strerror(errno)); quit(23); } if (nlen != len) { fprintf(stderr, "top: sysctl(%s...) expected %lu, got %lu\n", name, (unsigned long)len, (unsigned long)nlen); quit(23); } } static const char * format_nice(const struct kinfo_proc *pp) { const char *fifo, *kproc; int rtpri; static char nicebuf[4 + 1]; fifo = PRI_NEED_RR(pp->ki_pri.pri_class) ? "" : "F"; kproc = (pp->ki_flag & P_KPROC) ? "k" : ""; switch (PRI_BASE(pp->ki_pri.pri_class)) { case PRI_ITHD: return ("-"); case PRI_REALTIME: /* * XXX: the kernel doesn't tell us the original rtprio and * doesn't really know what it was, so to recover it we * must be more chummy with the implementation than the * implementation is with itself. pri_user gives a * constant "base" priority, but is only initialized * properly for user threads. pri_native gives what the * kernel calls the "base" priority, but it isn't constant * since it is changed by priority propagation. pri_native * also isn't properly initialized for all threads, but it * is properly initialized for kernel realtime and idletime * threads. Thus we use pri_user for the base priority of * user threads (it is always correct) and pri_native for * the base priority of kernel realtime and idletime threads * (there is nothing better, and it is usually correct). * * The field width and thus the buffer are too small for * values like "kr31F", but such values shouldn't occur, * and if they do then the tailing "F" is not displayed. */ rtpri = ((pp->ki_flag & P_KPROC) ? pp->ki_pri.pri_native : pp->ki_pri.pri_user) - PRI_MIN_REALTIME; snprintf(nicebuf, sizeof(nicebuf), "%sr%d%s", kproc, rtpri, fifo); break; case PRI_TIMESHARE: if (pp->ki_flag & P_KPROC) return ("-"); snprintf(nicebuf, sizeof(nicebuf), "%d", pp->ki_nice - NZERO); break; case PRI_IDLE: /* XXX: as above. */ rtpri = ((pp->ki_flag & P_KPROC) ? pp->ki_pri.pri_native : pp->ki_pri.pri_user) - PRI_MIN_IDLE; snprintf(nicebuf, sizeof(nicebuf), "%si%d%s", kproc, rtpri, fifo); break; default: return ("?"); } return (nicebuf); } /* comparison routines for qsort */ static int compare_pid(const void *p1, const void *p2) { const struct kinfo_proc * const *pp1 = p1; const struct kinfo_proc * const *pp2 = p2; if ((*pp2)->ki_pid < 0 || (*pp1)->ki_pid < 0) abort(); return ((*pp1)->ki_pid - (*pp2)->ki_pid); } static int compare_tid(const void *p1, const void *p2) { const struct kinfo_proc * const *pp1 = p1; const struct kinfo_proc * const *pp2 = p2; if ((*pp2)->ki_tid < 0 || (*pp1)->ki_tid < 0) abort(); return ((*pp1)->ki_tid - (*pp2)->ki_tid); } /* * proc_compare - comparison function for "qsort" * Compares the resource consumption of two processes using five * distinct keys. The keys (in descending order of importance) are: * percent cpu, cpu ticks, state, resident set size, total virtual * memory usage. The process states are ordered as follows (from least * to most important): WAIT, zombie, sleep, stop, start, run. The * array declaration below maps a process state index into a number * that reflects this ordering. */ static int sorted_state[] = { 0, /* not used */ 3, /* sleep */ 1, /* ABANDONED (WAIT) */ 6, /* run */ 5, /* start */ 2, /* zombie */ 4 /* stop */ }; #define ORDERKEY_PCTCPU(a, b) do { \ double diff; \ if (ps.wcpu) \ diff = weighted_cpu(PCTCPU((b)), (b)) - \ weighted_cpu(PCTCPU((a)), (a)); \ else \ diff = PCTCPU((b)) - PCTCPU((a)); \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) #define ORDERKEY_CPTICKS(a, b) do { \ int64_t diff = (int64_t)(b)->ki_runtime - (int64_t)(a)->ki_runtime; \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) #define ORDERKEY_STATE(a, b) do { \ int diff = sorted_state[(b)->ki_stat] - sorted_state[(a)->ki_stat]; \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) #define ORDERKEY_PRIO(a, b) do { \ int diff = (int)(b)->ki_pri.pri_level - (int)(a)->ki_pri.pri_level; \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) #define ORDERKEY_THREADS(a, b) do { \ int diff = (int)(b)->ki_numthreads - (int)(a)->ki_numthreads; \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) #define ORDERKEY_RSSIZE(a, b) do { \ long diff = (long)(b)->ki_rssize - (long)(a)->ki_rssize; \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) #define ORDERKEY_MEM(a, b) do { \ long diff = (long)PROCSIZE((b)) - (long)PROCSIZE((a)); \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) #define ORDERKEY_JID(a, b) do { \ int diff = (int)(b)->ki_jid - (int)(a)->ki_jid; \ if (diff != 0) \ return (diff > 0 ? 1 : -1); \ } while (0) +#define ORDERKEY_SWAP(a, b) do { \ + int diff = (int)ki_swap(b) - (int)ki_swap(a); \ + if (diff != 0) \ + return (diff > 0 ? 1 : -1); \ +} while (0) + /* compare_cpu - the comparison function for sorting by cpu percentage */ int #ifdef ORDER compare_cpu(void *arg1, void *arg2) #else proc_compare(void *arg1, void *arg2) #endif { struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; ORDERKEY_PCTCPU(p1, p2); ORDERKEY_CPTICKS(p1, p2); ORDERKEY_STATE(p1, p2); ORDERKEY_PRIO(p1, p2); ORDERKEY_RSSIZE(p1, p2); ORDERKEY_MEM(p1, p2); return (0); } #ifdef ORDER /* "cpu" compare routines */ int compare_size(), compare_res(), compare_time(), compare_prio(), compare_threads(); /* * "io" compare routines. Context switches aren't i/o, but are displayed * on the "io" display. */ int compare_iototal(), compare_ioread(), compare_iowrite(), compare_iofault(), compare_vcsw(), compare_ivcsw(); int (*compares[])() = { compare_cpu, compare_size, compare_res, compare_time, compare_prio, compare_threads, compare_iototal, compare_ioread, compare_iowrite, compare_iofault, compare_vcsw, compare_ivcsw, compare_jid, + compare_swap, NULL }; /* compare_size - the comparison function for sorting by total memory usage */ int compare_size(void *arg1, void *arg2) { struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; ORDERKEY_MEM(p1, p2); ORDERKEY_RSSIZE(p1, p2); ORDERKEY_PCTCPU(p1, p2); ORDERKEY_CPTICKS(p1, p2); ORDERKEY_STATE(p1, p2); ORDERKEY_PRIO(p1, p2); return (0); } /* compare_res - the comparison function for sorting by resident set size */ int compare_res(void *arg1, void *arg2) { struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; ORDERKEY_RSSIZE(p1, p2); ORDERKEY_MEM(p1, p2); ORDERKEY_PCTCPU(p1, p2); ORDERKEY_CPTICKS(p1, p2); ORDERKEY_STATE(p1, p2); ORDERKEY_PRIO(p1, p2); return (0); } /* compare_time - the comparison function for sorting by total cpu time */ int compare_time(void *arg1, void *arg2) { struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; ORDERKEY_CPTICKS(p1, p2); ORDERKEY_PCTCPU(p1, p2); ORDERKEY_STATE(p1, p2); ORDERKEY_PRIO(p1, p2); ORDERKEY_RSSIZE(p1, p2); ORDERKEY_MEM(p1, p2); return (0); } /* compare_prio - the comparison function for sorting by priority */ int compare_prio(void *arg1, void *arg2) { struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; ORDERKEY_PRIO(p1, p2); ORDERKEY_CPTICKS(p1, p2); ORDERKEY_PCTCPU(p1, p2); ORDERKEY_STATE(p1, p2); ORDERKEY_RSSIZE(p1, p2); ORDERKEY_MEM(p1, p2); return (0); } /* compare_threads - the comparison function for sorting by threads */ int compare_threads(void *arg1, void *arg2) { struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; ORDERKEY_THREADS(p1, p2); ORDERKEY_PCTCPU(p1, p2); ORDERKEY_CPTICKS(p1, p2); ORDERKEY_STATE(p1, p2); ORDERKEY_PRIO(p1, p2); ORDERKEY_RSSIZE(p1, p2); ORDERKEY_MEM(p1, p2); return (0); } /* compare_jid - the comparison function for sorting by jid */ static int compare_jid(const void *arg1, const void *arg2) { struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; ORDERKEY_JID(p1, p2); + ORDERKEY_PCTCPU(p1, p2); + ORDERKEY_CPTICKS(p1, p2); + ORDERKEY_STATE(p1, p2); + ORDERKEY_PRIO(p1, p2); + ORDERKEY_RSSIZE(p1, p2); + ORDERKEY_MEM(p1, p2); + + return (0); +} + +/* compare_swap - the comparison function for sorting by swap */ +static int +compare_swap(const void *arg1, const void *arg2) +{ + struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; + struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; + + ORDERKEY_SWAP(p1, p2); ORDERKEY_PCTCPU(p1, p2); ORDERKEY_CPTICKS(p1, p2); ORDERKEY_STATE(p1, p2); ORDERKEY_PRIO(p1, p2); ORDERKEY_RSSIZE(p1, p2); ORDERKEY_MEM(p1, p2); return (0); } #endif /* ORDER */ /* assorted comparison functions for sorting by i/o */ int #ifdef ORDER compare_iototal(void *arg1, void *arg2) #else io_compare(void *arg1, void *arg2) #endif { struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; return (get_io_total(p2) - get_io_total(p1)); } #ifdef ORDER int compare_ioread(void *arg1, void *arg2) { struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; long dummy, inp1, inp2; (void) get_io_stats(p1, &inp1, &dummy, &dummy, &dummy, &dummy); (void) get_io_stats(p2, &inp2, &dummy, &dummy, &dummy, &dummy); return (inp2 - inp1); } int compare_iowrite(void *arg1, void *arg2) { struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; long dummy, oup1, oup2; (void) get_io_stats(p1, &dummy, &oup1, &dummy, &dummy, &dummy); (void) get_io_stats(p2, &dummy, &oup2, &dummy, &dummy, &dummy); return (oup2 - oup1); } int compare_iofault(void *arg1, void *arg2) { struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; long dummy, flp1, flp2; (void) get_io_stats(p1, &dummy, &dummy, &flp1, &dummy, &dummy); (void) get_io_stats(p2, &dummy, &dummy, &flp2, &dummy, &dummy); return (flp2 - flp1); } int compare_vcsw(void *arg1, void *arg2) { struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; long dummy, flp1, flp2; (void) get_io_stats(p1, &dummy, &dummy, &dummy, &flp1, &dummy); (void) get_io_stats(p2, &dummy, &dummy, &dummy, &flp2, &dummy); return (flp2 - flp1); } int compare_ivcsw(void *arg1, void *arg2) { struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; long dummy, flp1, flp2; (void) get_io_stats(p1, &dummy, &dummy, &dummy, &dummy, &flp1); (void) get_io_stats(p2, &dummy, &dummy, &dummy, &dummy, &flp2); return (flp2 - flp1); } #endif /* ORDER */ /* * proc_owner(pid) - returns the uid that owns process "pid", or -1 if * the process does not exist. * It is EXTREMELY IMPORTANT that this function work correctly. * If top runs setuid root (as in SVR4), then this function * is the only thing that stands in the way of a serious * security problem. It validates requests for the "kill" * and "renice" commands. */ int proc_owner(int pid) { int cnt; struct kinfo_proc **prefp; struct kinfo_proc *pp; prefp = pref; cnt = pref_len; while (--cnt >= 0) { pp = *prefp++; if (pp->ki_pid == (pid_t)pid) return ((int)pp->ki_ruid); } return (-1); } static int swapmode(int *retavail, int *retfree) { int n; int pagesize = getpagesize(); struct kvm_swap swapary[1]; *retavail = 0; *retfree = 0; #define CONVERT(v) ((quad_t)(v) * pagesize / 1024) n = kvm_getswapinfo(kd, swapary, 1, 0); if (n < 0 || swapary[0].ksw_total == 0) return (0); *retavail = CONVERT(swapary[0].ksw_total); *retfree = CONVERT(swapary[0].ksw_total - swapary[0].ksw_used); n = (int)(swapary[0].ksw_used * 100.0 / swapary[0].ksw_total); return (n); } Index: projects/clang390-import/usr.sbin/bsdinstall/partedit/gpart_ops.c =================================================================== --- projects/clang390-import/usr.sbin/bsdinstall/partedit/gpart_ops.c (revision 305430) +++ projects/clang390-import/usr.sbin/bsdinstall/partedit/gpart_ops.c (revision 305431) @@ -1,1400 +1,1414 @@ /*- * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include "partedit.h" #define GPART_FLAGS "x" /* Do not commit changes by default */ static void gpart_show_error(const char *title, const char *explanation, const char *errstr) { char *errmsg; char message[512]; int error; if (explanation == NULL) explanation = ""; error = strtol(errstr, &errmsg, 0); if (errmsg != errstr) { while (errmsg[0] == ' ') errmsg++; if (errmsg[0] != '\0') sprintf(message, "%s%s. %s", explanation, strerror(error), errmsg); else sprintf(message, "%s%s", explanation, strerror(error)); } else { sprintf(message, "%s%s", explanation, errmsg); } dialog_msgbox(title, message, 0, 0, TRUE); } static int scheme_supports_labels(const char *scheme) { if (strcmp(scheme, "APM") == 0) return (1); if (strcmp(scheme, "GPT") == 0) return (1); if (strcmp(scheme, "PC98") == 0) return (1); return (0); } static void newfs_command(const char *fstype, char *command, int use_default) { if (strcmp(fstype, "freebsd-ufs") == 0) { int i; DIALOG_LISTITEM items[] = { {"UFS1", "UFS Version 1", "Use version 1 of the UFS file system instead " "of version 2 (not recommended)", 0 }, {"SU", "Softupdates", "Enable softupdates (default)", 1 }, {"SUJ", "Softupdates journaling", "Enable file system journaling (default - " "turn off for SSDs)", 1 }, {"TRIM", "Enable SSD TRIM support", "Enable TRIM support, useful on solid-state drives", 0 }, }; if (!use_default) { int choice; choice = dlg_checklist("UFS Options", "", 0, 0, 0, sizeof(items)/sizeof(items[0]), items, NULL, FLAG_CHECK, &i); if (choice == 1) /* Cancel */ return; } strcpy(command, "newfs "); for (i = 0; i < (int)(sizeof(items)/sizeof(items[0])); i++) { if (items[i].state == 0) continue; if (strcmp(items[i].name, "UFS1") == 0) strcat(command, "-O1 "); else if (strcmp(items[i].name, "SU") == 0) strcat(command, "-U "); else if (strcmp(items[i].name, "SUJ") == 0) strcat(command, "-j "); else if (strcmp(items[i].name, "TRIM") == 0) strcat(command, "-t "); } } else if (strcmp(fstype, "freebsd-zfs") == 0) { int i; DIALOG_LISTITEM items[] = { {"fletcher4", "checksum algorithm: fletcher4", "Use fletcher4 for data integrity checking. " "(default)", 1 }, {"fletcher2", "checksum algorithm: fletcher2", "Use fletcher2 for data integrity checking. " "(not recommended)", 0 }, {"sha256", "checksum algorithm: sha256", "Use sha256 for data integrity checking. " "(not recommended)", 0 }, {"atime", "Update atimes for files", "Disable atime update", 0 }, }; if (!use_default) { int choice; choice = dlg_checklist("ZFS Options", "", 0, 0, 0, sizeof(items)/sizeof(items[0]), items, NULL, FLAG_CHECK, &i); if (choice == 1) /* Cancel */ return; } strcpy(command, "zpool create -f -m none "); if (getenv("BSDINSTALL_TMPBOOT") != NULL) { char zfsboot_path[MAXPATHLEN]; sprintf(zfsboot_path, "%s/zfs", getenv("BSDINSTALL_TMPBOOT")); mkdir(zfsboot_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); sprintf(command, "%s -o cachefile=%s/zpool.cache ", command, zfsboot_path); } for (i = 0; i < (int)(sizeof(items)/sizeof(items[0])); i++) { if (items[i].state == 0) continue; if (strcmp(items[i].name, "fletcher4") == 0) strcat(command, "-O checksum=fletcher4 "); else if (strcmp(items[i].name, "fletcher2") == 0) strcat(command, "-O checksum=fletcher2 "); else if (strcmp(items[i].name, "sha256") == 0) strcat(command, "-O checksum=sha256 "); else if (strcmp(items[i].name, "atime") == 0) strcat(command, "-O atime=off "); } } else if (strcmp(fstype, "fat32") == 0 || strcmp(fstype, "efi") == 0) { int i; DIALOG_LISTITEM items[] = { {"FAT32", "FAT Type 32", "Create a FAT32 filesystem (default)", 1 }, {"FAT16", "FAT Type 16", "Create a FAT16 filesystem", 0 }, {"FAT12", "FAT Type 12", "Create a FAT12 filesystem", 0 }, }; if (!use_default) { int choice; choice = dlg_checklist("FAT Options", "", 0, 0, 0, sizeof(items)/sizeof(items[0]), items, NULL, FLAG_RADIO, &i); if (choice == 1) /* Cancel */ return; } strcpy(command, "newfs_msdos "); for (i = 0; i < (int)(sizeof(items)/sizeof(items[0])); i++) { if (items[i].state == 0) continue; if (strcmp(items[i].name, "FAT32") == 0) strcat(command, "-F 32 "); else if (strcmp(items[i].name, "FAT16") == 0) strcat(command, "-F 16 "); else if (strcmp(items[i].name, "FAT12") == 0) strcat(command, "-F 12 "); } } else { if (!use_default) dialog_msgbox("Error", "No configurable options exist " "for this filesystem.", 0, 0, TRUE); command[0] = '\0'; } } const char * choose_part_type(const char *def_scheme) { int cancel, choice; const char *scheme = NULL; DIALOG_LISTITEM items[] = { {"APM", "Apple Partition Map", "Bootable on PowerPC Apple Hardware", 0 }, {"BSD", "BSD Labels", "Bootable on most x86 systems", 0 }, {"GPT", "GUID Partition Table", "Bootable on most x86 systems and EFI aware ARM64", 0 }, {"MBR", "DOS Partitions", "Bootable on most x86 systems", 0 }, {"PC98", "NEC PC9801 Partition Table", "Bootable on NEC PC9801 systems", 0 }, {"VTOC8", "Sun VTOC8 Partition Table", "Bootable on Sun SPARC systems", 0 }, }; parttypemenu: dialog_vars.default_item = __DECONST(char *, def_scheme); cancel = dlg_menu("Partition Scheme", "Select a partition scheme for this volume:", 0, 0, 0, sizeof(items) / sizeof(items[0]), items, &choice, NULL); dialog_vars.default_item = NULL; if (cancel) return NULL; if (!is_scheme_bootable(items[choice].name)) { char message[512]; sprintf(message, "This partition scheme (%s) is not " "bootable on this platform. Are you sure you want " "to proceed?", items[choice].name); dialog_vars.defaultno = TRUE; cancel = dialog_yesno("Warning", message, 0, 0); dialog_vars.defaultno = FALSE; if (cancel) /* cancel */ goto parttypemenu; } scheme = items[choice].name; return scheme; } int gpart_partition(const char *lg_name, const char *scheme) { int cancel; struct gctl_req *r; const char *errstr; schememenu: if (scheme == NULL) { scheme = choose_part_type(default_scheme()); if (scheme == NULL) return (-1); if (!is_scheme_bootable(scheme)) { char message[512]; sprintf(message, "This partition scheme (%s) is not " "bootable on this platform. Are you sure you want " "to proceed?", scheme); dialog_vars.defaultno = TRUE; cancel = dialog_yesno("Warning", message, 0, 0); dialog_vars.defaultno = FALSE; if (cancel) { /* cancel */ /* Reset scheme so user can choose another */ scheme = NULL; goto schememenu; } } } r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, lg_name); gctl_ro_param(r, "flags", -1, GPART_FLAGS); gctl_ro_param(r, "scheme", -1, scheme); gctl_ro_param(r, "verb", -1, "create"); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') { gpart_show_error("Error", NULL, errstr); gctl_free(r); scheme = NULL; goto schememenu; } gctl_free(r); if (bootcode_path(scheme) != NULL) get_part_metadata(lg_name, 1)->bootcode = 1; return (0); } static void gpart_activate(struct gprovider *pp) { struct gconfig *gc; struct gctl_req *r; const char *errstr, *scheme; const char *attribute = NULL; intmax_t idx; /* * Some partition schemes need this partition to be marked 'active' * for it to be bootable. */ LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) { if (strcmp(gc->lg_name, "scheme") == 0) { scheme = gc->lg_val; break; } } if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "EBR") == 0 || strcmp(scheme, "PC98") == 0) attribute = "active"; else return; LIST_FOREACH(gc, &pp->lg_config, lg_config) { if (strcmp(gc->lg_name, "index") == 0) { idx = atoi(gc->lg_val); break; } } r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name); gctl_ro_param(r, "verb", -1, "set"); gctl_ro_param(r, "attrib", -1, attribute); gctl_ro_param(r, "index", sizeof(idx), &idx); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') gpart_show_error("Error", "Error marking partition active:", errstr); gctl_free(r); } void gpart_set_root(const char *lg_name, const char *attribute) { struct gctl_req *r; const char *errstr; r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, lg_name); gctl_ro_param(r, "flags", -1, "C"); gctl_ro_param(r, "verb", -1, "set"); gctl_ro_param(r, "attrib", -1, attribute); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') gpart_show_error("Error", "Error setting parameter on disk:", errstr); gctl_free(r); } static void gpart_bootcode(struct ggeom *gp) { const char *bootcode; struct gconfig *gc; struct gctl_req *r; const char *errstr, *scheme; uint8_t *boot; size_t bootsize, bytes; int bootfd; /* * Write default bootcode to the newly partitioned disk, if that * applies on this platform. */ LIST_FOREACH(gc, &gp->lg_config, lg_config) { if (strcmp(gc->lg_name, "scheme") == 0) { scheme = gc->lg_val; break; } } bootcode = bootcode_path(scheme); if (bootcode == NULL) return; bootfd = open(bootcode, O_RDONLY); if (bootfd < 0) { dialog_msgbox("Bootcode Error", strerror(errno), 0, 0, TRUE); return; } bootsize = lseek(bootfd, 0, SEEK_END); boot = malloc(bootsize); lseek(bootfd, 0, SEEK_SET); bytes = 0; while (bytes < bootsize) bytes += read(bootfd, boot + bytes, bootsize - bytes); close(bootfd); r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, gp->lg_name); gctl_ro_param(r, "verb", -1, "bootcode"); gctl_ro_param(r, "bootcode", bootsize, boot); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') gpart_show_error("Bootcode Error", NULL, errstr); gctl_free(r); free(boot); } static void gpart_partcode(struct gprovider *pp, const char *fstype) { struct gconfig *gc; const char *scheme; const char *indexstr; char message[255], command[255]; LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) { if (strcmp(gc->lg_name, "scheme") == 0) { scheme = gc->lg_val; break; } } /* Make sure this partition scheme needs partcode on this platform */ if (partcode_path(scheme, fstype) == NULL) return; LIST_FOREACH(gc, &pp->lg_config, lg_config) { if (strcmp(gc->lg_name, "index") == 0) { indexstr = gc->lg_val; break; } } /* Shell out to gpart for partcode for now */ sprintf(command, "gpart bootcode -p %s -i %s %s", partcode_path(scheme, fstype), indexstr, pp->lg_geom->lg_name); if (system(command) != 0) { sprintf(message, "Error installing partcode on partition %s", pp->lg_name); dialog_msgbox("Error", message, 0, 0, TRUE); } } void gpart_destroy(struct ggeom *lg_geom) { struct gctl_req *r; struct gprovider *pp; const char *errstr; int force = 1; /* Delete all child metadata */ LIST_FOREACH(pp, &lg_geom->lg_provider, lg_provider) gpart_delete(pp); /* Revert any local changes to get this geom into a pristine state */ r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, lg_geom->lg_name); gctl_ro_param(r, "verb", -1, "undo"); gctl_issue(r); /* Ignore errors -- these are non-fatal */ gctl_free(r); /* Now destroy the geom itself */ r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, lg_geom->lg_name); gctl_ro_param(r, "flags", -1, GPART_FLAGS); gctl_ro_param(r, "force", sizeof(force), &force); gctl_ro_param(r, "verb", -1, "destroy"); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') { /* * Check if we reverted away the existence of the geom * altogether. Show all other errors to the user. */ if (strtol(errstr, NULL, 0) != EINVAL) gpart_show_error("Error", NULL, errstr); } gctl_free(r); /* And any metadata associated with the partition scheme itself */ delete_part_metadata(lg_geom->lg_name); } void gpart_edit(struct gprovider *pp) { struct gctl_req *r; struct gconfig *gc; struct gconsumer *cp; struct ggeom *geom; const char *errstr, *oldtype, *scheme; struct partition_metadata *md; char sizestr[32]; char newfs[255]; intmax_t idx; int hadlabel, choice, junk, nitems; unsigned i; DIALOG_FORMITEM items[] = { {0, "Type:", 5, 0, 0, FALSE, "", 11, 0, 12, 15, 0, FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, " "freebsd-swap)", FALSE}, {0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 0, 0, FALSE, "Partition size. Append K, M, G for kilobytes, " "megabytes or gigabytes.", FALSE}, {0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0, FALSE, "Path at which to mount this partition (leave blank " "for swap, set to / for root filesystem)", FALSE}, {0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE, "Partition name. Not all partition schemes support this.", FALSE}, }; /* * Find the PART geom we are manipulating. This may be a consumer of * this provider, or its parent. Check the consumer case first. */ geom = NULL; LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { /* Check for zombie geoms, treating them as blank */ scheme = NULL; LIST_FOREACH(gc, &cp->lg_geom->lg_config, lg_config) { if (strcmp(gc->lg_name, "scheme") == 0) { scheme = gc->lg_val; break; } } if (scheme == NULL || strcmp(scheme, "(none)") == 0) { gpart_partition(cp->lg_geom->lg_name, NULL); return; } /* If this is a nested partition, edit as usual */ if (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) break; /* Destroy the geom and all sub-partitions */ gpart_destroy(cp->lg_geom); /* Now re-partition and return */ gpart_partition(cp->lg_geom->lg_name, NULL); return; } if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) geom = pp->lg_geom; if (geom == NULL) { /* Disk not partitioned, so partition it */ gpart_partition(pp->lg_name, NULL); return; } LIST_FOREACH(gc, &geom->lg_config, lg_config) { if (strcmp(gc->lg_name, "scheme") == 0) { scheme = gc->lg_val; break; } } nitems = scheme_supports_labels(scheme) ? 4 : 3; /* Edit editable parameters of a partition */ hadlabel = 0; LIST_FOREACH(gc, &pp->lg_config, lg_config) { if (strcmp(gc->lg_name, "type") == 0) { oldtype = gc->lg_val; items[0].text = gc->lg_val; } if (strcmp(gc->lg_name, "label") == 0 && gc->lg_val != NULL) { hadlabel = 1; items[3].text = gc->lg_val; } if (strcmp(gc->lg_name, "index") == 0) idx = atoi(gc->lg_val); } TAILQ_FOREACH(md, &part_metadata, metadata) { if (md->name != NULL && strcmp(md->name, pp->lg_name) == 0) { if (md->fstab != NULL) items[2].text = md->fstab->fs_file; break; } } humanize_number(sizestr, 7, pp->lg_mediasize, "B", HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL); items[1].text = sizestr; editpart: choice = dlg_form("Edit Partition", "", 0, 0, 0, nitems, items, &junk); if (choice) /* Cancel pressed */ goto endedit; + /* If this is the root partition, check that this fs is bootable */ + if (strcmp(items[2].text, "/") == 0 && !is_fs_bootable(scheme, + items[0].text)) { + char message[512]; + sprintf(message, "This file system (%s) is not bootable " + "on this system. Are you sure you want to proceed?", + items[0].text); + dialog_vars.defaultno = TRUE; + choice = dialog_yesno("Warning", message, 0, 0); + dialog_vars.defaultno = FALSE; + if (choice == 1) /* cancel */ + goto editpart; + } + /* Check if the label has a / in it */ if (strchr(items[3].text, '/') != NULL) { dialog_msgbox("Error", "Label contains a /, which is not an " "allowed character.", 0, 0, TRUE); goto editpart; } r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, geom->lg_name); gctl_ro_param(r, "flags", -1, GPART_FLAGS); gctl_ro_param(r, "verb", -1, "modify"); gctl_ro_param(r, "index", sizeof(idx), &idx); if (hadlabel || items[3].text[0] != '\0') gctl_ro_param(r, "label", -1, items[3].text); gctl_ro_param(r, "type", -1, items[0].text); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') { gpart_show_error("Error", NULL, errstr); gctl_free(r); goto editpart; } gctl_free(r); newfs_command(items[0].text, newfs, 1); set_default_part_metadata(pp->lg_name, scheme, items[0].text, items[2].text, (strcmp(oldtype, items[0].text) != 0) ? newfs : NULL); endedit: if (strcmp(oldtype, items[0].text) != 0 && cp != NULL) gpart_destroy(cp->lg_geom); if (strcmp(oldtype, items[0].text) != 0 && strcmp(items[0].text, "freebsd") == 0) gpart_partition(pp->lg_name, "BSD"); for (i = 0; i < (sizeof(items) / sizeof(items[0])); i++) if (items[i].text_free) free(items[i].text); } void set_default_part_metadata(const char *name, const char *scheme, const char *type, const char *mountpoint, const char *newfs) { struct partition_metadata *md; char *zpool_name = NULL; int i; /* Set part metadata */ md = get_part_metadata(name, 1); if (newfs) { if (md->newfs != NULL) { free(md->newfs); md->newfs = NULL; } if (newfs != NULL && newfs[0] != '\0') { md->newfs = malloc(strlen(newfs) + strlen(" /dev/") + strlen(mountpoint) + 5 + strlen(name) + 1); if (strcmp("freebsd-zfs", type) == 0) { zpool_name = strdup((strlen(mountpoint) == 1) ? "root" : &mountpoint[1]); for (i = 0; zpool_name[i] != 0; i++) if (!isalnum(zpool_name[i])) zpool_name[i] = '_'; sprintf(md->newfs, "%s %s /dev/%s", newfs, zpool_name, name); } else { sprintf(md->newfs, "%s /dev/%s", newfs, name); } } } if (strcmp(type, "freebsd-swap") == 0) mountpoint = "none"; if (strcmp(type, bootpart_type(scheme)) == 0) md->bootcode = 1; /* VTOC8 needs partcode at the start of partitions */ if (strcmp(scheme, "VTOC8") == 0 && (strcmp(type, "freebsd-ufs") == 0 || strcmp(type, "freebsd-zfs") == 0)) md->bootcode = 1; if (mountpoint == NULL || mountpoint[0] == '\0') { if (md->fstab != NULL) { free(md->fstab->fs_spec); free(md->fstab->fs_file); free(md->fstab->fs_vfstype); free(md->fstab->fs_mntops); free(md->fstab->fs_type); free(md->fstab); md->fstab = NULL; } } else { if (md->fstab == NULL) { md->fstab = malloc(sizeof(struct fstab)); } else { free(md->fstab->fs_spec); free(md->fstab->fs_file); free(md->fstab->fs_vfstype); free(md->fstab->fs_mntops); free(md->fstab->fs_type); } if (strcmp("freebsd-zfs", type) == 0) { md->fstab->fs_spec = strdup(zpool_name); } else { md->fstab->fs_spec = malloc(strlen(name) + strlen("/dev/") + 1); sprintf(md->fstab->fs_spec, "/dev/%s", name); } md->fstab->fs_file = strdup(mountpoint); /* Get VFS from text after freebsd-, if possible */ if (strncmp("freebsd-", type, 8) == 0) md->fstab->fs_vfstype = strdup(&type[8]); else if (strcmp("fat32", type) == 0 || strcmp("efi", type) == 0) md->fstab->fs_vfstype = strdup("msdosfs"); else md->fstab->fs_vfstype = strdup(type); /* Guess */ if (strcmp(type, "freebsd-swap") == 0) { md->fstab->fs_type = strdup(FSTAB_SW); md->fstab->fs_freq = 0; md->fstab->fs_passno = 0; } else if (strcmp(type, "freebsd-zfs") == 0) { md->fstab->fs_type = strdup(FSTAB_RW); md->fstab->fs_freq = 0; md->fstab->fs_passno = 0; } else { md->fstab->fs_type = strdup(FSTAB_RW); if (strcmp(mountpoint, "/") == 0) { md->fstab->fs_freq = 1; md->fstab->fs_passno = 1; } else { md->fstab->fs_freq = 2; md->fstab->fs_passno = 2; } } md->fstab->fs_mntops = strdup(md->fstab->fs_type); } if (zpool_name != NULL) free(zpool_name); } static int part_compare(const void *xa, const void *xb) { struct gprovider **a = (struct gprovider **)xa; struct gprovider **b = (struct gprovider **)xb; intmax_t astart, bstart; struct gconfig *gc; astart = bstart = 0; LIST_FOREACH(gc, &(*a)->lg_config, lg_config) if (strcmp(gc->lg_name, "start") == 0) { astart = strtoimax(gc->lg_val, NULL, 0); break; } LIST_FOREACH(gc, &(*b)->lg_config, lg_config) if (strcmp(gc->lg_name, "start") == 0) { bstart = strtoimax(gc->lg_val, NULL, 0); break; } if (astart < bstart) return -1; else if (astart > bstart) return 1; else return 0; } intmax_t gpart_max_free(struct ggeom *geom, intmax_t *npartstart) { struct gconfig *gc; struct gprovider *pp, **providers; intmax_t sectorsize, stripesize, offset; intmax_t lastend; intmax_t start, end; intmax_t maxsize, maxstart; intmax_t partstart, partend; int i, nparts; /* Now get the maximum free size and free start */ start = end = 0; LIST_FOREACH(gc, &geom->lg_config, lg_config) { if (strcmp(gc->lg_name, "first") == 0) start = strtoimax(gc->lg_val, NULL, 0); if (strcmp(gc->lg_name, "last") == 0) end = strtoimax(gc->lg_val, NULL, 0); } i = nparts = 0; LIST_FOREACH(pp, &geom->lg_provider, lg_provider) nparts++; providers = calloc(nparts, sizeof(providers[0])); LIST_FOREACH(pp, &geom->lg_provider, lg_provider) providers[i++] = pp; qsort(providers, nparts, sizeof(providers[0]), part_compare); lastend = start - 1; maxsize = 0; for (i = 0; i < nparts; i++) { pp = providers[i]; LIST_FOREACH(gc, &pp->lg_config, lg_config) { if (strcmp(gc->lg_name, "start") == 0) partstart = strtoimax(gc->lg_val, NULL, 0); if (strcmp(gc->lg_name, "end") == 0) partend = strtoimax(gc->lg_val, NULL, 0); } if (partstart - lastend > maxsize) { maxsize = partstart - lastend - 1; maxstart = lastend + 1; } lastend = partend; } if (end - lastend > maxsize) { maxsize = end - lastend - 1; maxstart = lastend + 1; } pp = LIST_FIRST(&geom->lg_consumer)->lg_provider; /* * Round the start and size of the largest available space up to * the nearest multiple of the adjusted stripe size. * * The adjusted stripe size is the least common multiple of the * actual stripe size, or the sector size if no stripe size was * reported, and 4096. The reason for this is that contemporary * disks often have 4096-byte physical sectors but report 512 * bytes instead for compatibility with older / broken operating * systems and BIOSes. For the same reasons, virtualized storage * may also report a 512-byte stripe size, or none at all. */ sectorsize = pp->lg_sectorsize; if ((stripesize = pp->lg_stripesize) == 0) stripesize = sectorsize; while (stripesize % 4096 != 0) stripesize *= 2; if ((offset = maxstart * sectorsize % stripesize) != 0) { offset = (stripesize - offset) / sectorsize; maxstart += offset; maxsize -= offset; } if (npartstart != NULL) *npartstart = maxstart; return (maxsize); } void gpart_create(struct gprovider *pp, char *default_type, char *default_size, char *default_mountpoint, char **partname, int interactive) { struct gctl_req *r; struct gconfig *gc; struct gconsumer *cp; struct ggeom *geom; const char *errstr, *scheme; char sizestr[32], startstr[32], output[64], *newpartname; char newfs[255], options_fstype[64]; intmax_t maxsize, size, sector, firstfree, stripe; uint64_t bytes; int nitems, choice, junk; unsigned i; DIALOG_FORMITEM items[] = { {0, "Type:", 5, 0, 0, FALSE, "freebsd-ufs", 11, 0, 12, 15, 0, FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, " "freebsd-swap)", FALSE}, {0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 15, 0, FALSE, "Partition size. Append K, M, G for kilobytes, " "megabytes or gigabytes.", FALSE}, {0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0, FALSE, "Path at which to mount partition (blank for " "swap, / for root filesystem)", FALSE}, {0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE, "Partition name. Not all partition schemes support this.", FALSE}, }; if (partname != NULL) *partname = NULL; /* Record sector and stripe sizes */ sector = pp->lg_sectorsize; stripe = pp->lg_stripesize; /* * Find the PART geom we are manipulating. This may be a consumer of * this provider, or its parent. Check the consumer case first. */ geom = NULL; LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { geom = cp->lg_geom; break; } if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) geom = pp->lg_geom; /* Now get the partition scheme */ scheme = NULL; if (geom != NULL) { LIST_FOREACH(gc, &geom->lg_config, lg_config) if (strcmp(gc->lg_name, "scheme") == 0) scheme = gc->lg_val; } if (geom == NULL || scheme == NULL || strcmp(scheme, "(none)") == 0) { if (gpart_partition(pp->lg_name, NULL) == 0) dialog_msgbox("", "The partition table has been successfully created." " Please press Create again to create partitions.", 0, 0, TRUE); return; } /* * If we still don't have a geom, either the user has * canceled partitioning or there has been an error which has already * been displayed, so bail. */ if (geom == NULL) return; maxsize = size = gpart_max_free(geom, &firstfree); if (size <= 0) { dialog_msgbox("Error", "No free space left on device.", 0, 0, TRUE); return; } humanize_number(sizestr, 7, size*sector, "B", HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL); items[1].text = sizestr; /* Special-case the MBR default type for nested partitions */ if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "PC98") == 0) { items[0].text = "freebsd"; items[0].help = "Filesystem type (e.g. freebsd, fat32)"; } nitems = scheme_supports_labels(scheme) ? 4 : 3; if (default_type != NULL) items[0].text = default_type; if (default_size != NULL) items[1].text = default_size; if (default_mountpoint != NULL) items[2].text = default_mountpoint; /* Default options */ strncpy(options_fstype, items[0].text, sizeof(options_fstype)); newfs_command(options_fstype, newfs, 1); addpartform: if (interactive) { dialog_vars.extra_label = "Options"; dialog_vars.extra_button = TRUE; choice = dlg_form("Add Partition", "", 0, 0, 0, nitems, items, &junk); dialog_vars.extra_button = FALSE; switch (choice) { case 0: /* OK */ break; case 1: /* Cancel */ return; case 3: /* Options */ strncpy(options_fstype, items[0].text, sizeof(options_fstype)); newfs_command(options_fstype, newfs, 0); goto addpartform; } } /* * If the user changed the fs type after specifying options, undo * their choices in favor of the new filesystem's defaults. */ if (strcmp(options_fstype, items[0].text) != 0) { strncpy(options_fstype, items[0].text, sizeof(options_fstype)); newfs_command(options_fstype, newfs, 1); } size = maxsize; if (strlen(items[1].text) > 0) { if (expand_number(items[1].text, &bytes) != 0) { char error[512]; sprintf(error, "Invalid size: %s\n", strerror(errno)); dialog_msgbox("Error", error, 0, 0, TRUE); goto addpartform; } size = MIN((intmax_t)(bytes/sector), maxsize); } /* Check if the label has a / in it */ if (strchr(items[3].text, '/') != NULL) { dialog_msgbox("Error", "Label contains a /, which is not an " "allowed character.", 0, 0, TRUE); goto addpartform; } /* Warn if no mountpoint set */ if (strcmp(items[0].text, "freebsd-ufs") == 0 && items[2].text[0] != '/') { dialog_vars.defaultno = TRUE; choice = dialog_yesno("Warning", "This partition does not have a valid mountpoint " "(for the partition from which you intend to boot the " "operating system, the mountpoint should be /). Are you " "sure you want to continue?" , 0, 0); dialog_vars.defaultno = FALSE; if (choice == 1) /* cancel */ goto addpartform; } /* * Error if this scheme needs nested partitions, this is one, and * a mountpoint was set. */ if (strcmp(items[0].text, "freebsd") == 0 && strlen(items[2].text) > 0) { dialog_msgbox("Error", "Partitions of type \"freebsd\" are " "nested BSD-type partition schemes and cannot have " "mountpoints. After creating one, select it and press " "Create again to add the actual file systems.", 0, 0, TRUE); goto addpartform; } /* If this is the root partition, check that this scheme is bootable */ if (strcmp(items[2].text, "/") == 0 && !is_scheme_bootable(scheme)) { char message[512]; sprintf(message, "This partition scheme (%s) is not bootable " "on this platform. Are you sure you want to proceed?", scheme); dialog_vars.defaultno = TRUE; choice = dialog_yesno("Warning", message, 0, 0); dialog_vars.defaultno = FALSE; if (choice == 1) /* cancel */ goto addpartform; } /* If this is the root partition, check that this fs is bootable */ if (strcmp(items[2].text, "/") == 0 && !is_fs_bootable(scheme, items[0].text)) { char message[512]; sprintf(message, "This file system (%s) is not bootable " "on this system. Are you sure you want to proceed?", items[0].text); dialog_vars.defaultno = TRUE; choice = dialog_yesno("Warning", message, 0, 0); dialog_vars.defaultno = FALSE; if (choice == 1) /* cancel */ goto addpartform; } /* * If this is the root partition, and we need a boot partition, ask * the user to add one. */ /* Check for existing freebsd-boot partition */ LIST_FOREACH(pp, &geom->lg_provider, lg_provider) { struct partition_metadata *md; md = get_part_metadata(pp->lg_name, 0); if (md == NULL || !md->bootcode) continue; LIST_FOREACH(gc, &pp->lg_config, lg_config) if (strcmp(gc->lg_name, "type") == 0) break; if (gc != NULL && strcmp(gc->lg_val, bootpart_type(scheme)) == 0) break; } /* If there isn't one, and we need one, ask */ if ((strcmp(items[0].text, "freebsd") == 0 || strcmp(items[2].text, "/") == 0) && bootpart_size(scheme) > 0 && pp == NULL) { if (interactive) choice = dialog_yesno("Boot Partition", "This partition scheme requires a boot partition " "for the disk to be bootable. Would you like to " "make one now?", 0, 0); else choice = 0; if (choice == 0) { /* yes */ r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, geom->lg_name); gctl_ro_param(r, "flags", -1, GPART_FLAGS); gctl_ro_param(r, "verb", -1, "add"); gctl_ro_param(r, "type", -1, bootpart_type(scheme)); snprintf(sizestr, sizeof(sizestr), "%jd", bootpart_size(scheme) / sector); gctl_ro_param(r, "size", -1, sizestr); snprintf(startstr, sizeof(startstr), "%jd", firstfree); gctl_ro_param(r, "start", -1, startstr); gctl_rw_param(r, "output", sizeof(output), output); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') gpart_show_error("Error", NULL, errstr); gctl_free(r); get_part_metadata(strtok(output, " "), 1)->bootcode = 1; /* Now adjust the part we are really adding forward */ firstfree += bootpart_size(scheme) / sector; size -= (bootpart_size(scheme) + stripe)/sector; if (stripe > 0 && (firstfree*sector % stripe) != 0) firstfree += (stripe - ((firstfree*sector) % stripe)) / sector; } } r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, geom->lg_name); gctl_ro_param(r, "flags", -1, GPART_FLAGS); gctl_ro_param(r, "verb", -1, "add"); gctl_ro_param(r, "type", -1, items[0].text); snprintf(sizestr, sizeof(sizestr), "%jd", size); gctl_ro_param(r, "size", -1, sizestr); snprintf(startstr, sizeof(startstr), "%jd", firstfree); gctl_ro_param(r, "start", -1, startstr); if (items[3].text[0] != '\0') gctl_ro_param(r, "label", -1, items[3].text); gctl_rw_param(r, "output", sizeof(output), output); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') { gpart_show_error("Error", NULL, errstr); gctl_free(r); goto addpartform; } newpartname = strtok(output, " "); gctl_free(r); /* * Try to destroy any geom that gpart picked up already here from * dirty blocks. */ r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, newpartname); gctl_ro_param(r, "flags", -1, GPART_FLAGS); junk = 1; gctl_ro_param(r, "force", sizeof(junk), &junk); gctl_ro_param(r, "verb", -1, "destroy"); gctl_issue(r); /* Error usually expected and non-fatal */ gctl_free(r); if (strcmp(items[0].text, bootpart_type(scheme)) == 0) get_part_metadata(newpartname, 1)->bootcode = 1; else if (strcmp(items[0].text, "freebsd") == 0) gpart_partition(newpartname, "BSD"); else set_default_part_metadata(newpartname, scheme, items[0].text, items[2].text, newfs); for (i = 0; i < (sizeof(items) / sizeof(items[0])); i++) if (items[i].text_free) free(items[i].text); if (partname != NULL) *partname = strdup(newpartname); } void gpart_delete(struct gprovider *pp) { struct gconfig *gc; struct ggeom *geom; struct gconsumer *cp; struct gctl_req *r; const char *errstr; intmax_t idx; int is_partition; /* Is it a partition? */ is_partition = (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0); /* Find out if this is the root of a gpart geom */ geom = NULL; LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { geom = cp->lg_geom; break; } /* If so, destroy all children */ if (geom != NULL) { gpart_destroy(geom); /* If this is a partition, revert it, so it can be deleted */ if (is_partition) { r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, geom->lg_name); gctl_ro_param(r, "verb", -1, "undo"); gctl_issue(r); /* Ignore non-fatal errors */ gctl_free(r); } } /* * If this is not a partition, see if that is a problem, complain if * necessary, and return always, since we need not do anything further, * error or no. */ if (!is_partition) { if (geom == NULL) dialog_msgbox("Error", "Only partitions can be deleted.", 0, 0, TRUE); return; } r = gctl_get_handle(); gctl_ro_param(r, "class", -1, pp->lg_geom->lg_class->lg_name); gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name); gctl_ro_param(r, "flags", -1, GPART_FLAGS); gctl_ro_param(r, "verb", -1, "delete"); LIST_FOREACH(gc, &pp->lg_config, lg_config) { if (strcmp(gc->lg_name, "index") == 0) { idx = atoi(gc->lg_val); gctl_ro_param(r, "index", sizeof(idx), &idx); break; } } errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') { gpart_show_error("Error", NULL, errstr); gctl_free(r); return; } gctl_free(r); delete_part_metadata(pp->lg_name); } void gpart_revert_all(struct gmesh *mesh) { struct gclass *classp; struct gconfig *gc; struct ggeom *gp; struct gctl_req *r; const char *modified; LIST_FOREACH(classp, &mesh->lg_class, lg_class) { if (strcmp(classp->lg_name, "PART") == 0) break; } if (strcmp(classp->lg_name, "PART") != 0) { dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE); return; } LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { modified = "true"; /* XXX: If we don't know (kernel too old), * assume there are modifications. */ LIST_FOREACH(gc, &gp->lg_config, lg_config) { if (strcmp(gc->lg_name, "modified") == 0) { modified = gc->lg_val; break; } } if (strcmp(modified, "false") == 0) continue; r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, gp->lg_name); gctl_ro_param(r, "verb", -1, "undo"); gctl_issue(r); gctl_free(r); } } void gpart_commit(struct gmesh *mesh) { struct partition_metadata *md; struct gclass *classp; struct ggeom *gp; struct gconfig *gc; struct gconsumer *cp; struct gprovider *pp; struct gctl_req *r; const char *errstr; const char *modified; const char *rootfs; LIST_FOREACH(classp, &mesh->lg_class, lg_class) { if (strcmp(classp->lg_name, "PART") == 0) break; } /* Figure out what filesystem / uses */ rootfs = "ufs"; /* Assume ufs if nothing else present */ TAILQ_FOREACH(md, &part_metadata, metadata) { if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0) { rootfs = md->fstab->fs_vfstype; break; } } if (strcmp(classp->lg_name, "PART") != 0) { dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE); return; } LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { modified = "true"; /* XXX: If we don't know (kernel too old), * assume there are modifications. */ LIST_FOREACH(gc, &gp->lg_config, lg_config) { if (strcmp(gc->lg_name, "modified") == 0) { modified = gc->lg_val; break; } } if (strcmp(modified, "false") == 0) continue; /* Add bootcode if necessary, before the commit */ md = get_part_metadata(gp->lg_name, 0); if (md != NULL && md->bootcode) gpart_bootcode(gp); /* Now install partcode on its partitions, if necessary */ LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { md = get_part_metadata(pp->lg_name, 0); if (md == NULL || !md->bootcode) continue; /* Mark this partition active if that's required */ gpart_activate(pp); /* Check if the partition has sub-partitions */ LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) break; if (cp == NULL) /* No sub-partitions */ gpart_partcode(pp, rootfs); } r = gctl_get_handle(); gctl_ro_param(r, "class", -1, "PART"); gctl_ro_param(r, "arg0", -1, gp->lg_name); gctl_ro_param(r, "verb", -1, "commit"); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') gpart_show_error("Error", NULL, errstr); gctl_free(r); } } Index: projects/clang390-import =================================================================== --- projects/clang390-import (revision 305430) +++ projects/clang390-import (revision 305431) Property changes on: projects/clang390-import ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r305397-305430